Причина огромного размера скомпилированного исполняемого файла Go
Я выполнил программу hello world Go, которая сгенерировала собственный исполняемый файл на моей Linux-машине. Но я был удивлен, увидев размер простой программы Hello world Go, это было 1.9MB!
Почему это так, что исполняемый файл такой простой программы в Go настолько огромен?
Ответы
Ответ 1
Этот точный вопрос появляется в официальном FAQ: Почему моя тривиальная программа такая большая двоичная? p >
Цитирование ответа:
Линкеры в цепочке инструментов gc (5l
, 6l
и 8l
) выполняют статические ссылки. Таким образом, все бинарные файлы Go включают в себя время выполнения Go вместе с информацией типа времени выполнения, необходимой для поддержки проверок динамического типа, отражения и даже трассировки стека паники.
Простая программа C "hello, world", скомпилированная и связанная статически с использованием gcc в Linux, составляет около 750 kB, включая реализацию printf
. Эквивалентная программа Go с использованием fmt.Printf
составляет около 1,9 МБ, но включает более мощную поддержку и информацию о времени выполнения.
Таким образом, собственный исполняемый файл вашего Hello World составляет 1,9 МБ, поскольку он содержит среду выполнения, которая предоставляет сборку мусора, отражение и многие другие функции (которые ваша программа может не использовать, но там). И реализация пакета fmt
, который вы использовали для печати текста "Hello World"
(плюс его зависимости).
Теперь попробуйте следующее: добавьте еще одну строку fmt.Println("Hello World! Again")
в свою программу и скомпилируйте ее снова. Результат не будет 2x 1,9 МБ, но все равно всего 1,9 МБ! Да, потому что все используемые библиотеки (fmt
и его зависимости) и среда выполнения уже добавлены в исполняемый файл (и поэтому для печати второго текста, который вы только что добавили, будет добавлено только несколько байт).
Ответ 2
Обратите внимание, что проблема двоичного размера отслеживается проблемой 6853 в golang/go.
Например, совершить a26c01a (для Go 1.4) сократить мир привет на 70kB:
потому что мы не записываем эти имена в таблицу символов.
Учитывая компилятор, ассемблер, компоновщик и среда выполнения для 1.5 будут
полностью в Go, вы можете ожидать дальнейшей оптимизации.
Ответ 3
Рассмотрим следующую программу:
package main
import "fmt"
func main() {
fmt.Println("Hello World!")
}
Если я построю это на моей машине AMD64 (Go 1.9), вот так:
$ go build
$ ls -la helloworld
-rwxr-xr-x 1 janf group 2029206 Sep 11 16:58 helloworld
Я получаю двоичный файл размером около 2 МБ.
Причина этого (что объясняется в других ответах) заключается в том, что мы используем пакет "fmt", который довольно велик, но двоичный файл также не был удален, а это означает, что таблица символов все еще существует. Если мы вместо этого попросим компилятор удалить бинарный файл, он станет намного меньше:
$ go build -ldflags "-s -w"
$ ls -la helloworld
-rwxr-xr-x 1 janf group 1323616 Sep 11 17:01 helloworld
Однако, если мы перепишем программу для использования встроенной функции print, вместо fmt.Println, выполните следующие действия:
package main
func main() {
print("Hello World!\n")
}
И затем скомпилируйте его:
$ go build -ldflags "-s -w"
$ ls -la helloworld
-rwxr-xr-x 1 janf group 714176 Sep 11 17:06 helloworld
В итоге мы получим еще меньшую двоичную. Это настолько мало, что мы можем получить его, не прибегая к трюкам, таким как UPX-упаковка, поэтому накладные расходы Go-runtime составляют около 700 Kb.