предисловие
В этой статье в основном рассказывается о выделении памяти и управлении памятью в Go, а также немного рассказывается о выделении и освобождении памяти, а также о сборке мусора в Go. С очень крупной точки зрения управление памятью в Go выглядит так, как показано на рисунке ниже.Сегодня мы в основном сосредоточимся на части, отмеченной красным.
Язык Go отказывается от того, как разработчики управляют памятью в C/C++, реализует активное управление приложениями и активным выпуском, добавляет escape-анализ и GC, освобождает разработчиков от управления памятью и позволяет разработчикам больше сосредоточиться на разработке программного обеспечения, а не на основных проблемах с памятью. Это одна из причин, почему Go — высокопродуктивный язык.
Нам не нужно разбираться в управлении памятью, потому что это действительно сложно, но освоение управления памятью позволит вам писать код более высокого качества, а также поможет вам находить ошибки. В этой статье используется поэтапный подход, и в свою очередь будут представлены базовые знания о хранилище, TCMalloc, «предшественнике» управления памятью Go, затем об управлении и распределении памяти Go и, наконец, краткое изложение. Цель этого состоит в том, чтобы надеяться, что вы сможете лучше мыслить в области кодирования и архитектурного мышления благодаря общему пониманию и мышлению.
текст
1. Обзор основ хранения
В этой части мы кратко рассмотрим систему хранения компьютера, виртуальную память, стек и кучу, а также управление памятью в куче.Эта часть важна для понимания и освоения управления памятью в Go.
1.1 Пирамида хранения
Эта картина выражает систему хранения данных компьютера, скорость доступа сверху вниз становится все медленнее и медленнее, а время доступа становится все длиннее и длиннее. Сверху вниз расположены:
- регистры процессора
- CPU Cache
- ОЗУ
- Вспомогательные устройства хранения, такие как жесткие диски
- Внешние устройства, такие как мышь
Задумывались ли вы когда-нибудь над следующими двумя простыми вопросами, если нет, подумайте об этом:
- Если ЦП напрямую обращается к жесткому диску, можно ли полностью использовать ЦП?
- Если ЦП напрямую обращается к памяти, можно ли полностью использовать ЦП?
Скорость ЦП высокая, но постоянное хранилище, такое как жесткий диск, очень медленное.Если ЦП напрямую обращается к диску, диск может замедлить скорость ЦП, и общая производительность машины будет низкой.Чтобы сделать из-за разницы в скорости между двумя аппаратными средствами, процессором и между дисками добавляется гораздо более быстрая память, чем диски.
Однако скорость процессора и памяти неодинакова.Как видно из приведенного выше рисунка, скорость процессора быстро увеличивается (закон Мура), но скорость памяти увеличивается очень медленно, хотя скорость процессора растет очень медленно, однако скорость памяти не сильно увеличилась, и разрыв в скорости очень велик. С 1980 года разрыв между скоростью процессора и памяти постоянно увеличивается. Чтобы компенсировать разницу в скорости между этими два аппаратных обеспечения, соотношение между процессором и памятью было увеличено Кэш с более быстрой памятью Кэш - это кеш данных памяти, который может сократить время, необходимое ЦП для доступа к памяти.
Трехуровневые кэши: L1, L2 и L3. Их скорости имеют три разных уровня. Скорость L1 является самой быстрой и наиболее близкой к скорости ЦП, которая в 100 раз превышает скорость ОЗУ. Скорость L2 снижается до 25 раз. что из ОЗУ Скорость ближе к скорости ОЗУ.
Увидев это, у вас получился многоуровневый дизайн всей системы хранения? Сверху вниз скорость становится все ниже и ниже, а время доступа становится все больше и больше.От диска к регистру ЦП верхний уровень можно рассматривать как кеш следующего уровня. После прочтения многоуровневого дизайна следует формальное введение в память.
1.2 Виртуальная память
Виртуальная память является важной функцией современных операционных систем.Для процессов виртуальная память защищает основную оперативную память и диск и предоставляет процессам пространство памяти, намного превышающее размер физической памяти. Давайте посмотрим на иерархическую структуру виртуальной памяти.
На приведенном выше рисунке показан процесс доступа к виртуальной памяти для получения данных, когда процесс обращается к данным, когда кэш пропускает попадание. При доступе к памяти происходит фактический доступ к виртуальной памяти.Виртуальная память проверяется через таблицу страниц, чтобы увидеть, был ли адрес виртуальной памяти, к которому осуществляется доступ в данный момент, загружен в физическую память. Если он уже находится в физической памяти, извлекаются данные из физической памяти.Если соответствующей физической памяти нет, данные загружаются с диска в физическую память, а адрес физической памяти и адрес виртуальной памяти обновляются в таблице страниц. .
Физическая память — это уровень кэширования дискового хранилища.В эпоху отсутствия виртуальной памяти физическая память совместно используется всеми процессами, и при одновременном доступе нескольких процессов к одной и той же физической памяти будут возникать проблемы параллелизма. После введения виртуальной памяти каждый процесс имеет свою собственную виртуальную память, а степень детализации одновременного доступа к памяти может быть снижена с уровня нескольких процессов до уровня многопоточности.
1.3 Стек и куча
Теперь мы начинаем с виртуальной памяти и идем на один уровень дальше, чтобы увидеть стек и кучу в виртуальной памяти, то есть управление памятью процессом.
На приведенном выше рисунке показано разделение виртуальной памяти процесса.Адреса памяти, используемые в коде, являются адресами виртуальной памяти, а не фактическими адресами физической памяти. Стек и куча — это всего лишь две области памяти в виртуальной памяти с разными функциями:
-
Стек находится по старшему адресу, растет от старшего адреса к младшему.
-
Куча находится по нижнему адресу и растет от младшего адреса к старшему.
Стек имеет ряд преимуществ перед кучей:
- Управление памятью в стеке простое, а выделение происходит быстрее, чем в куче.
- Память стека не нужно перерабатывать, но куча должна быть переработана, будь то активная свободная или пассивная сборка мусора, которая требует дополнительного ЦП.
- Память в стеке имеет лучшую локальность, а доступ к памяти в куче не такой дружественный.Две части данных, к которым обращается ЦП, могут находиться на разных страницах, и время доступа ЦП к данным может увеличиться.
1.4 Управление динамической памятью
Давайте сделаем еще один шаг. Когда мы говорим об управлении памятью, это в основном относится к управлению памятью кучи, потому что управление памятью стека не требует, чтобы программа беспокоилась об этом. В этом разделе мы увидим, что имеет управление памятью кучи. удавшийся. Как показано на рисунке выше, в основном есть три части, а именно выделение блоков памяти, освобождение блоков памяти и организация блоков памяти.
В одном из простейших методов управления памятью куча изначально представляла бы собой полный фрагмент, т. е. память не выделялась. Когда приложение памяти будет найдено, память кучи будет разделена на небольшой блок памяти (блок) из нераспределенной памяти, а затем все блоки памяти будут связаны связным списком. Некоторая информация требуется для описания основной информации о каждом блоке памяти, такой как размер (размер), используется ли он (используется) и адрес следующего блока памяти (следующий).Фактические данные блока памяти хранятся в данных.
Блок памяти содержит три типа информации, как показано на рисунке ниже, метаданные, пользовательские данные и поля выравнивания.Выравнивание памяти предназначено для повышения эффективности доступа. При подаче заявки на 5-байтовую память на следующем рисунке требуется выравнивание памяти.
Суть освобождения памяти заключается в том, чтобы вынуть из связанного списка используемый блок памяти, а затем пометить его как неиспользуемый.При выделении блока памяти можно сначала найти блок памяти аналогичного размера из неиспользуемого блока памяти. память в нераспределенной памяти.
Проблема фрагментации памяти не рассматривалась в приведенном выше простом дизайне, потому что при непрерывном применении и освобождении памяти в памяти будет большое количество фрагментов, что снизит скорость использования памяти. Чтобы решить проблему фрагментации памяти, можно объединить два последовательных неиспользуемых блока памяти, чтобы уменьшить фрагментацию.
Вышеизложенное является основной идеей управления памятью.Если вы хотите узнать больше об основах управления памятью, вы можете прочитать эту статью «Написание распределителя памяти».Три картинки в этом разделе также взяты из этой статьи.
2. TCMalloc
TCMalloc - это аббревиатура от Thread Cache Malloc, которая является источником управления памятью Go. Управление памятью Go основано на TCMalloc. С итерацией Go несоответствие между управлением памятью Go и TCMalloc постоянно расширяется, но его основные идеи, принципы и концепции Все они совместимы с TCMalloc.Если вы пропустите TCMalloc и сразу перейдете к управлению памятью в Go, вы можете запутаться.
Овладение концепцией TCMalloc может заложить прочную основу для освоения управления памятью в Go, не уделяя слишком много внимания деталям исходного кода.
В операционной системе Linux на самом деле существует множество библиотек управления памятью, например, ptmalloc из glibc, jemalloc из FreeBSD, tcmalloc из Google и т. д. Почему так много библиотек управления памятью? Суть в том, чтобы добиться более высокой эффективности управления памятью при многопоточном программировании: главной целью является более быстрое выделение памяти.
Ранее мы упоминали, что после введения виртуальной памяти степень детализации проблем одновременного доступа к памяти снижается с уровня нескольких процессов до уровня нескольких потоков. Однако все потоки в рамках одного процесса совместно используют одно и то же пространство памяти. Когда они обращаются за памятью, они должны быть заблокированы. Если они не заблокированы, возникает проблема, связанная с тем, что к одной и той же памяти обращаются два потока одновременно.
Какова практика TCMalloc? Предварительно выделяйте кеш для каждого потока. Когда поток обращается за небольшой памятью, он может выделить память из кеша. Это имеет два преимущества:
-
1 системный вызов требуется для предварительного выделения кеша для потоков.Когда последующие потоки обращаются за небольшой памятью, они выделяются непосредственно из кеша, которые все выполняются в пользовательском режиме.Без системных вызовов общее выделение и время освобождения памяти укорачивается, что является быстрым выделением.Второй уровень памяти.
-
Когда несколько потоков обращаются за небольшой памятью одновременно, они обращаются к разным адресным пространствам из своих соответствующих кешей, поэтому нет необходимости в блокировке, а степень детализации одновременного доступа к памяти еще больше снижается.Это третий уровень быстрого выделения памяти. .
2.1. Основные принципы
Ниже приводится краткое введение в TCMalloc, достаточно подробное для понимания управления памятью в Go.
В сочетании с приведенным выше рисунком представлены несколько важных концепций TCMalloc:
- Page
Операционная система управляет памятью в единицах страниц, как и TCMalloc, но размер страницы в TCMalloc не обязательно равен размеру в операционной системе, а кратен. В «Расшифровке TCMalloc» сказано, что размер страницы под x64 составляет 8 КБ.
- Span
Группа смежных страниц называется промежутками. Например, могут быть промежутки размером в 2 страницы или промежутки размером в 16 страниц. Промежутки на один уровень выше, чем страницы, чтобы облегчить управление областями памяти определенного размера. в TCMalloc Основная единица управления памятью.
- ThreadCache
ThreadCache — это кэш каждого потока. Кэш содержит несколько связанных списков свободных блоков памяти. Каждый связанный список связан с блоком памяти. Размер блоков памяти в одном и том же связанном списке одинаков. Можно также сказать, что в соответствии с размером блока памяти, данные Блоки памяти разделены на категории, так что свободные блоки памяти могут быть быстро выбраны из соответствующего связанного списка в соответствии с запрошенным объемом памяти. Поскольку каждый поток имеет свой собственный ThreadCache, доступ к ThreadCache осуществляется без блокировки.
- CentralCache
CentralCache — это кеш, общий для всех потоков, и сохраненный связанный список свободных блоков памяти.Количество связанных списков такое же, как количество связанных списков в ThreadCache.Когда блоков памяти ThreadCache недостаточно, блоки памяти можно получить из CentralCache; когда блоков памяти ThreadCache слишком много, блоки памяти могут быть сохранены обратно в CentralCache. Поскольку CentralCache является общим, доступ к нему заблокирован.
- PageHeap
PageHeap — это абстракция кучи памяти, PageHeap также хранит несколько связанных списков, а связанный список хранит Span. Когда памяти CentralCache недостаточно, он получает свободный Span памяти из PageHeap, а затем разбивает Span на несколько блоков памяти, добавляет его в связанный список соответствующего размера и выделяет память; когда память CentralCache слишком много, свободной памяти будет Блок помещается обратно в PageHeap.
Как показано на рисунке ниже, это список диапазонов из 1 страницы, список диапазонов из 2 страниц и т. д. и, наконец, набор больших диапазонов, который используется для сохранения средних и больших объектов. Нет сомнений, что PageHeap также заблокирован.
В предыдущей статье упоминались малые, средние и большие объекты.В Go есть похожие концепции управления памятью.Давайте посмотрим на определение TCMalloc:
- Размер малого объекта: 0 ~ 256 КБ
- Размер среднего объекта: 257~1 МБ
- Размер большого объекта:> 1 МБ
Процесс выделения небольших объектов: ThreadCache -> CentralCache -> HeapPage.В большинстве случаев кэша ThreadCache достаточно, и нет необходимости обращаться к CentralCache и HeapPage.Отсутствует системный вызов и выделение без блокировки, а эффективность распределения очень высока.
Процесс выделения среднего объекта: вы можете напрямую выбрать соответствующий размер в PageHeap.Максимальный объем памяти, сохраняемый Span из 128 страниц, составляет 1 МБ.
Процесс выделения больших объектов: выберите подходящее количество страниц из большого набора диапазонов, чтобы сформировать диапазон для хранения данных.
Через введение в этот раздел у вас должно быть определенное понимание основной идеи TCMalloc, я предлагаю ознакомиться с вышеприведенным содержанием.
3. Перейти к управлению памятью
Как упоминалось ранее, управление памятью Go является производным от TCMalloc, но у него есть еще две вещи, чем у TCMalloc: анализ побега и сборка мусора, которые являются двумя отличными инструментами для повышения производительности. В этой главе мы сначала познакомимся с управлением памятью Go и выделением памяти Go, и, наконец, рассмотрим небольшую сборку мусора и освобождение памяти.
3.1 Основные понятия управления памятью Go
Многие концепции управления памятью Go уже присутствуют в TCMalloc, смысл тот же, но названия изменились. Позвольте мне сначала дать вам макроизображение и представить его вместе с помощью изображения.
- Page
Так же, как Page в TCMalloc, размер 1 Page в архитектуре x64 составляет 8 КБ. В нижней части изображения выше светло-голубой прямоугольник представляет страницу.
- Span
Span — это то же самое, что и Span в TCMalloc. Span — это базовая единица управления памятью. В коде это mspan. Страниц 1 Span, кроме того, 1 сиреневый прямоугольник равен 1 Span.
- mcache
mcache похож на ThreadCache в TCMalloc.mcache хранит интервалы разного размера и классифицируется в соответствии с классом Span.Небольшим объектам выделяется память непосредственно из mcache, который действует как кеш и может быть доступен без блокировок. Однако mcache также отличается от ThreadCache: в TCMalloc на каждый поток приходится один ThreadCache, а в Go на каждый P приходится один mcache. Поскольку в программе Go в настоящее время выполняется не более потоков GOMAXPROCS, поэтому требуется не более mcache GOMAXPROCS, чтобы обеспечить свободный доступ к mcache каждым потоком, а выполнение потоков привязано к P, а mcache передается к П. только что хорошо.
- mcentral
Подобно CentralCache в TCMalloc, mcentral — это кеш, совместно используемый всеми потоками, и для него требуется доступ к блокировке. Он классифицирует пролеты в соответствии с уровнем промежутка, а затем объединяет их в связанный список.Когда выделяется память определенного уровня пролета в mcache, он будет применяться к mcentral для пролета текущего уровня.
Однако mcentral также отличается от CentralCache. CentralCache имеет один связанный список для каждого уровня Span, а mcache имеет два связанных списка для каждого уровня Span. Это связано с применением памяти mcache, которое мы объясним позже.
- mheap
mheap похож на PageHeap в TCMalloc.Это абстракция кучи памяти.Он организует страницы памяти, запрошенные ОС, в интервалы и сохраняет их. Когда диапазона mcentral недостаточно, он будет обращаться за памятью к mheap, а когда диапазона mheap недостаточно, он будет обращаться к ОС за памятью. Приложение памяти mheap к ОС основано на страницах, а затем применяемые страницы памяти организуются в промежутки, которые также требуют доступа к блокировке.
Но mheap также отличается от PageHeap: mheap организует Span в древовидную структуру, а не в связанный список, и по-прежнему представляет собой два дерева, а затем назначает Span для heapArena для управления, включая сопоставление адресов и определение того, содержит ли span растровое изображение, такое как указатель, поэтому основная причина этого — более эффективное использование памяти: выделение, переработка и повторное использование.
- размер объекта: в коде называется размером, относится к размеру объекта, применяемого для памяти.
- Класс размера: в коде он называется классом. Это уровень размера, который эквивалентен классификации размера в определенном интервале размера. Например, размер [1,8] принадлежит классу размера 1, а размер ( 8,16] относится ко 2 размерному классу.
- Класс пролета: относится к уровню пролета, но размер класса пролета не пропорционален размеру пролета. Класс span в основном используется для соответствия классу размера. Один класс размера соответствует двум классам span. Размер диапазона двух классов span одинаков, но функции разные. Один используется для хранения объектов, содержащих указатели, а другой используется для хранения объектов, которые не содержат указатели.Объект указателя, Span, который не содержит объект указателя, не нуждается в GC сканировании.
- количество страниц: обозначается в коде как npage, что представляет собой количество страниц, что на самом деле является количеством страниц, содержащихся в диапазоне, который используется для выделения памяти.
3.2 Распределение памяти в Go
Классификация памяти в Go не делится на малые, средние и большие объекты, как TCMalloc, но его малый объект подразделяется на крошечный объект, который относится к объектам размером от 1 до 16 байт и без указателей. Маленькие объекты и большие объекты разграничиваются только по размеру, никаких других различий не делается.
Маленькие объекты выделяются в mcache, а большие объекты выделяются непосредственно из mheap, из выделения памяти маленьких объектов.
3.1.1 Выделение памяти для небольших объектов
В этом разделе преобразования размера мы вводим таблицу преобразования.Существует 66 классов размера от 1 до 66. В коде _NumSizeClasses=67 представляет фактическое количество используемых классов размера, то есть 67, от 0 до 67, размер класс 0 фактически не используется.
Как было сказано выше, одному размерному классу соответствуют два класса пролетов:
numSpanClasses = _NumSizeClasses * 2
numSpanClasses означает, что количество классов охвата равно 134, поэтому нижние индексы классов охвата находятся в диапазоне от 0 до 133, поэтому классы охвата, отмеченные mcache на приведенном выше рисунке, относятся к интервалу от 0 до 133 класса. Каждый класс span указывает на диапазон, то есть mcache может иметь максимум 134 диапазона.
- Найти интервалы для объектов
Процесс поиска пролетов выглядит следующим образом:
- Рассчитать размер памяти, необходимой объекту
- Рассчитайте требуемый размерный класс в соответствии с сопоставлением размерного класса.
- Вычислить класс span на основе класса размера и того, содержит ли объект указатель
- Получить диапазон, на который указывает класс span
В качестве примера возьмем выделение объекта размером 24 байта, который не содержит указателей, в соответствии с таблицей сопоставления:
// class bytes/obj bytes/span objects tail waste max waste
// 1 8 8192 1024 0 87.50%
// 2 16 8192 512 0 43.75%
// 3 32 8192 256 0 46.88%
// 4 48 8192 170 32 31.52%
Соответствующий класс размера — 3, а диапазон размеров его объекта — (16,32] байт, а 24 байта — как раз в этом диапазоне, поэтому класс размера этого объекта равен 3.
Расчет класса Size для класса span выглядит следующим образом:
// noscan为true代表对象不包含指针
func makeSpanClass(sizeclass uint8, noscan bool) spanClass {
return spanClass(sizeclass<<1) | spanClass(bool2int(noscan))
}
Таким образом, соответствующий класс span равен 7, поэтому объекту нужен диапазон, на который указывает класс span 7.
span class = 3 << 1 | 1 = 7
- Выделить пространство объекта из диапазона
Диапазон можно разделить на множество частей в соответствии с размером объекта, который можно рассчитать из таблицы сопоставления.В качестве примера возьмите диапазон, соответствующий классу размера 3, размер диапазона составляет 8 КБ, а фактическое пространство, занимаемое каждый объект имеет размер 32 байта, этот диапазон делится на 256 блоков. Если имеется 256 блоков, адрес памяти каждого блока объекта может быть рассчитан в соответствии с начальным адресом диапазона.
При выделении памяти некоторые блоки памяти объекта в диапазоне заняты, а некоторые не заняты.Например, на приведенном выше рисунке все представляет собой один диапазон, синий блок представляет собой занятую память, а зеленый блок представляет собой незанятую объем памяти. При выделении памяти просто быстро найдите первый доступный зеленый блок, рассчитайте адрес памяти и при необходимости очистите данные блока памяти.
Когда все блоки памяти в диапазоне заняты и не осталось места для дальнейшего выделения объектов, mcache подаст заявку на получение диапазона в mcentral, и mcache продолжит выделение объектов после получения диапазона.
- mcache обращается за интервалом к mcentral
Как и mcache, mcentral имеет 134 уровня классов спанов от 0 до 133, но каждый уровень сохраняет 2 списка спанов, то есть 2 связанных списка спанов:
- непусто: для интервалов в этом связанном списке все интервалы имеют по крайней мере одно свободное пространство для объектов. Эти промежутки добавляются в связанный список, когда mcache освобождает промежутки.
- пусто: спаны в этом связанном списке, все спаны не уверены, есть ли в них свободное пространство для объектов. Когда диапазон будет передан mcache, он будет добавлен в пустой список.
Названия этих двух вещей всегда немного сбивали с толку.Рекомендуется прямо понимать пустой, так как нет пространства объекта.
Когда mcache запрашивает диапазон у mcentral, mcentral сначала ищет диапазон, соответствующий условиям, из непустого. передать найденный span в mcache.
- span управление mheap
В mheap сохраняются два двоичных дерева сортировки, отсортированные по количеству страниц в диапазоне:
- free: спаны, хранящиеся в free, являются бесплатными и не подвергаются сборке мусора.
- scav: scav хранит простаивающие интервалы и спаны со сборкой мусора.
Если span освобождается из-за сборки мусора, span будет добавлен в scav, в противном случае он будет добавлен в свободный, например, span, который также состоит из памяти, только что запрошенной из ОС.
В mheap также есть арены, которые состоят из группы heapArenas.Каждая heapArena содержит последовательные спаны pagePerArena, которые в основном используются для управления спанами mheap и службами сборки мусора. Сама mheap — глобальная переменная, и данные в ней — это тоже память, напрямую запрошенная из ОС, а не в той части памяти, которой управляет mheap.
- mcentral применяет span к mheap
Когда mcentral предоставляет диапазон для mcache, если в пустом нет подходящего диапазона, mcentral будет применять диапазон из mheap.
На этом этапе mcentral должен предоставить mheap необходимое количество страниц памяти и уровень класса спана, а затем он сначала ищет доступные спаны из свободных. Если он не найден, он будет искать доступные промежутки в файле scav. Если он не был найден, он будет обращаться за памятью к ОС, а затем повторно искать 2 дерева, и он обязательно найдет диапазон. Если найденный пролет больше, чем требуемый пролет, разделите пролет на 2 пролетов, один из которых как раз нужного размера, добавьте оставшиеся пролеты в свободные, а затем задайте базовую информацию о требуемых пролетах, а затем Дайте ее центральный.
- mheap запрашивает память у ОС
Когда mheap не хватает памяти, mheap запросит память у ОС, сохранит запрошенную страницу памяти как диапазон, а затем вставит этот диапазон в свободное дерево. В 32-битной системе mheap зарезервирует часть пространства.Когда в mheap нет места, сначала подать заявку на него из зарезервированного пространства.Если в зарезервированном пространстве больше нет памяти, обратиться к ОС.
3.1.2 Выделение памяти для больших объектов
Выделение больших объектов намного проще, чем мелких, процесс на 99% такой же, как и обращение mcentral к памяти из mheap, поэтому повторяться не будет. Разница в том, что mheap записывает некоторые статистические данные о больших объектах, подробности смотрите в mheap.alloc_m().
3.2 Сборка мусора и освобождение памяти
Если вы только выделяете и выделяете память, память в конечном итоге закончится. Go использует сборку мусора для сбора спанов, которые больше не используются, и вызывает mspan.scavenge(), чтобы освободить спаны для ОС (на самом деле не освобождая, просто сообщая ОС, что информация об этом фрагменте памяти бесполезна, если вы нужно, заберите обратно), затем передайте его в mheap, mheap объединяет спаны и добавляет объединенные спаны в дерево scav. В ожидании перераспределения памяти mheap выполняет перераспределение памяти. Go сборка мусора также является сильным тема, и ее планируется написать отдельно позже.Вступление к статье.
Теперь давайте сосредоточимся на том, как программа Go освобождает память для операционной системы? Для освобождения памяти используется функция sysUnused, которая вызывается mspan.scavenge():
func sysUnused(v unsafe.Pointer, n uintptr) {
// MADV_FREE_REUSABLE is like MADV_FREE except it also propagates
// accounting information about the process to task_info.
madvise(v, n, _MADV_FREE_REUSABLE)
}
В комментарии говорится, что _MADV_FREE_REUSABLE по функциям похож на MADV_FREE.Его функция состоит в том, чтобы предоставить ядру предложение: память в этом диапазоне адресов памяти больше не используется и может быть переработана. Но перезаряжается ли ядро и когда, это дело ядра. Если ядро действительно освобождает этот кусок памяти, когда программа Go снова использует этот адрес, ядро переназначит виртуальный адрес на физический адрес. Таким образом, в случае достаточного объема памяти ядру не нужно немедленно освобождать память.
4. Используйте стек памяти
Наконец, упомянем память стека. С точки зрения макросов управление памятью должно иметь не только кучи, но и стеки. Каждая горутина имеет свой собственный стек. Начальный размер стека 2 КБ. Один миллион горутин будет занимать 2 ГБ, но стек горутин будет автоматически расширяться, когда 2 КБ недостаточно. Когда стек расширится до 4 КБ, будет занимать один миллион горутин 4ГБ.
Суммировать
Принцип выделения памяти в Go больше рассматриваться не будет, в нем в основном подчеркиваются две важные идеи:
-
Используйте кеш для повышения эффективности. Идею кеша можно увидеть везде во всей системе хранения.Распределение и управление памятью Go также используют кеш.Использование кеша заключается в уменьшении количества системных вызовов, а второе – в уменьшении гранулярности блокировок и количества замков.Из этих двух точек, чтобы повысить эффективность управления памятью.
-
Обменяйте пространство на время, чтобы повысить эффективность управления памятью. Пространство во времени – распространенная идея оптимизации производительности. Эта идея на самом деле очень распространена. Например, суть структур данных, таких как хэш, карта и двоичное дерево сортировки, – пространство во времени, что также очень распространено в базы данных, такие как индексы и индексы базы данных, кэш представлений и данных и т. д., а также базы данных кеша, такие как Redis, также являются идеей изменения пространства во времени.
Эта учетная запись будет продолжать делиться сухими товарами серверных технологий, включая основы виртуальных машин, многопоточное программирование, высокопроизводительные фреймворки, асинхронное ПО, промежуточное ПО для кэширования и обмена сообщениями, распределенные и микросервисы, материалы для обучения архитектуре и расширенные учебные материалы и статьи.