Куда идут переменные Голанга?

Go

Студенты, писавшие на C/C++, знают, что вызов знаменитых функций malloc и new может выделить часть памяти в куче, ответственность за использование и уничтожение которой лежит на программисте. Если вы не будете осторожны, произойдет утечка памяти, которая вызовет у вас панику.

После перехода на Golang практически не нужно беспокоиться об утечках памяти. Хотя есть и новая функция, память, полученная с помощью новой функции, не обязательно находится в куче. Разница между кучей и стеком «нечетка» для программистов, разумеется, все это за нас делает компилятор Go за кулисами.

Является переменным выделенным на ворс или выделено на стеке, это компилятор逃逸分析После заключения.

Эта статья поможет вам вместе исследовать逃逸分析- Куда идут переменные, куча или стек?

Что такое анализ побега

Раньше при написании кода на C/C++ для повышения эффективности мы часто использовалиpass-by-value(передавать по значению) "обновить" доpass-by-reference, чтобы избежать запуска конструктора и напрямую вернуть указатель.

Вы должны помнить, что здесь скрыта большая дыра: внутри функции определяется локальная переменная, а затем возвращается адрес (указатель) этой локальной переменной. Эти локальные переменные размещаются в стеке (выделение статической памяти).После выполнения функции память, занятая переменными, будет уничтожена.Любое действие (например, разыменование) над этим возвращаемым значением нарушит работу программы , даже вызвать прямой сбой программы. Например следующий код:

int *foo ( void )   
{   
    int t = 3;
    return &t;
} 

Некоторые студенты могут знать вышеупомянутую яму и использовать более разумный подход: использовать новую функцию внутри функции для построения переменной (динамическое выделение памяти), а затем возвращать адрес этой переменной. Поскольку переменные создаются в куче, они не уничтожаются при выходе из функции. Но работает ли это? Когда и где следует удалить новый объект? Вызывающий может забыть удалить или передать возвращаемое значение напрямую другим функциям, а потом уже не может его удалить, то есть происходит утечка памяти. Что касается этой ямы, вы можете посмотреть статью 21 «Эффективного C++», которая очень хороша!

C++ признан языком с самым сложным синтаксисом, говорят, что никто не может полностью понять синтаксис C++. И все это сильно отличается в языке Go. Код C++, как в примере выше, без проблем вставляется в Go.

Ваш великолепный внешний вид должен поддерживаться множеством людей, стоящих за вами! Язык Go — это компилятор逃逸分析. Это оптимизация и упрощение управления памятью после того, как компилятор проведет статический анализ кода.

В принципе компиляции метод анализа динамического диапазона указателя называется逃逸分析.通俗来讲,当一个对象的指针被多个方法或线程引用时,我们称这个指针发生了逃逸。

Проще говоря,逃逸分析Определяет, где находится переменная: в куче или в стеке.

Зачем избегать анализа

Упомянутые ранее проблемы, возникающие в C/C++, высоко оцениваются как языковая особенность Go. Это действительно мёд мышьяка C/C++ Go!

Динамически выделяемую память в C/C++ необходимо освобождать вручную, что заставляет обезьян ходить по тонкому льду при написании программ. В этом есть свои преимущества: программист имеет полный контроль над памятью. Но есть и много минусов: забывание освободить память часто приводит к утечкам памяти. Поэтому во многие современные языки добавлены механизмы сборки мусора.

Идите с коллекцией мусора, так что куча и стек прозрачно для программиста. Настоящее освобождение рук программистов, поэтому они могут сосредоточиться на бизнесе, «эффективно» для завершения кодирования. Комплексные механизмы, которые управляют памятью компилятору, программисты могут пойти и наслаждаться жизнью.

逃逸分析Эта «дерзкая операция» разумно назначает переменные туда, куда она должна идти, и «находит свое собственное положение». Даже если вы используете new для применения к памяти, если я обнаружу, что вы бесполезны после выхода из функции, то я закину вас в стек, ведь выделение памяти в стеке намного быстрее, чем в куче; наоборот, даже если вы окажетесь Это просто обычная переменная, но после escape-анализа обнаружится, что после выхода из функции есть другие места, на которые ссылаются, то я выделю вас в кучу. Действительно добиться «распределения по потребностям» и осуществить коммунизм заранее!

Если переменные размещены в куче, в отличие от стека, куча может быть очищена автоматически. Это заставит Go выполнять частую сборку мусора, а сборка мусора потребует относительно больших системных накладных расходов (занимает 25% мощности ЦП).

По сравнению со стеком куча подходит для выделения памяти непредсказуемого размера. Но расплатой за это является более медленное выделение и фрагментация памяти. Выделение памяти стека происходит очень быстро. Память, выделенная стеком, нуждается только в двух инструкциях ЦП: «PUSH» и «RELEASSE», выделение и освобождение; в то время как памяти, выделенной кучей, сначала нужно найти блок памяти подходящего размера, а затем он может быть освобожден через сборку мусора.

Посредством escape-анализа переменные, которые не нужно выделять в куче, можно максимально напрямую выделить в стек.В куче меньше переменных, что снизит накладные расходы на выделение памяти в куче, снизит нагрузку на gc , и улучшить скорость работы программы.

Как проводится анализ побега

Самый основной принцип escape-анализа Go заключается в том, что если функция возвращает ссылку на переменную, то она экранируется.

Проще говоря, компилятор анализирует характеристики кода и жизненный цикл кода.Переменные в Go размещаются в стеке только в том случае, если компилятор может доказать, что на них не будут ссылаться после возврата функции.В остальных случаях они выделено для нагромождения.

Язык Go не является ключевым словом или функцией, которые могут быть назначены непосредственно переменным в куче компилятора, наоборот, компилятор решает, где назначать переменные, анализируя код.

Взятие адреса переменной может быть выделено в куче. Однако после того, как компилятор выполнит escape-анализ, если обнаружится, что на эту переменную не будут ссылаться после возврата из функции, она все равно будет размещена в стеке. Настроить адрес персонажа, просто хотите накрутить субсидию? Слишком молод!

Проще говоря, компилятор решает, экранировать ли переменную, основываясь на том, ссылаются ли на нее извне:

  1. Если вне функции нет ссылки, она будет помещена в стек первой;
  2. Если есть ссылка вне функции, она должна быть помещена в кучу;

В первом случае он может быть размещен в куче: определен большой массив, и память, которую необходимо применить, слишком велика, что превышает емкость стека.

Пример анализа побега

Go предоставляет связанные команды, чтобы увидеть, если переменная выходит.

Или используйте пример, который мы упоминали выше:

package main

import "fmt"

func foo() *int {
	t := 3
	return &t;
}

func main() {
	x := foo()
	fmt.Println(*x)
}

Функция foo возвращает указатель на локальную переменную, которую получает переменная x в основной функции. Выполните следующую команду:

go build -gcflags '-m -l' main.go

добавлять-lзаключается в том, чтобы функция foo не была встроена. Получил следующий вывод:

# command-line-arguments
src/main.go:7:9: &t escapes to heap
src/main.go:6:7: moved to heap: t
src/main.go:12:14: *x escapes to heap
src/main.go:12:13: main ... argument does not escape

переменная в функции footСбежали, как мы и предполагали. Чего мы не понимаем, так это почему в основной функцииxтоже сбежал? Это связано с тем, что некоторые параметры функции имеют интерфейсный тип, например fmt.Println(a ...interface{}), во время компиляции трудно определить конкретный тип его параметров, и также могут возникать escape-последовательности.

Вы также можете использовать команду дизассемблирования, чтобы увидеть, исчезает ли переменная.

go tool compile -S main.go

Перехват некоторых результатов, описание которых отмечено на рисункеtПамять была выделена в куче, и произошел побег.

反汇编

Суммировать

Динамически выделяемая память в куче намного дороже, чем статически выделяемая память в стеке.

Переменная, размещенная в стеке, должна иметь возможность определять свою область во время компиляции, иначе они будут размещены в куче.

Компилятор Go проверит область действия переменной во время компиляции и выполнит ряд проверок.Если ее область всегда известна компилятору во время выполнения, она будет размещена в стеке.

Проще говоря, компилятор решает, экранировать или нет, в зависимости от того, ссылаются на переменную извне или нет. Программистам Go эти правила escape-анализа компилятора не нужно осваивать, нам нужно только пройтиgo build -gcflags '-m'команда для наблюдения за ситуацией выхода переменной.

Не используйте слепо указатель на переменную в качестве параметра функции, хотя это и уменьшит количество операций копирования. Но на самом деле, когда параметром является сама переменная, копирование выполняется в стек, и накладные расходы намного меньше, чем динамическое выделение памяти в куче после выхода переменной.

Наконец, постарайтесь написать как можно меньше управляющих кодов, чтобы повысить эффективность работы программы.

QR

использованная литература

【Как произошел побег? Очень хорошо, в конце много ссылок] https://www.do1618.com/archives/1328/go-%E5%86%85%E5%AD%98%E9%80%83%E9%80% B8%E8 %AF%A6%E7%BB%86%E5%88%86%E6%9E%90/

[Переменные Go в конце выделенной кучи или стека] https://github.com/developer-learning/night-reading-go/blob/master/content/discuss/2018-07-09-make-new-in-go .мд

[Понимание стека Golang] https://segmentfault.com/a/1190000017498101

[Escape Analysis Написание рекомендаций по распределению памяти в стеке] https://segment.com/blog/allocation-efficiency-in-high-performance-go-services/ [Анализ побегов относительно краток] https://studygolang.com/articles/17584

[Определение анализа выхода] https://cloud.tencent.com/developer/article/1117410

[Пример анализа побега] https://my.oschina.net/renhc/blog/2222104

https://gocn.vip/article/355 [Справочник кода сборки] https://github.com/maniafish/about_go/blob/master/heap_stack.md

[Дефекты анализа побега] https://studygolang.com/articles/12396

[Хороший пример анализа побега] http://www.agardner.me/golang/grast/collection/gc/escape/analysys/2015/10/18/go-escape-analysis.html