Under the Hood of .NET Memory Management / Под капотом у памяти в .Net: Chapter 2: The Simple Heap Model

← →
Английский оригинал Перевод на русский

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

В главе 1 мы увидели как стек используется для хранения типов значений и указателей на объекты ссылочных типов, которые хранятся в куче. Это позволяет каждому вызванному методу сохранять состояние локальных переменных между вызовами других методов. Мы также обсудили устройство самой кучи и как происходит создание, выделение памяти и уничтожение объектов управляемых .NET во время исполнения.

masterofsea147 20.11.19 в 03:12

0

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

masterofsea147 20.11.19 в 03:17

0

Управляемые кучи
Напомним, что при запуске приложения .NET два раздела памяти резервируются для хранения объектов, размещаемых во время выполнения (на данный момент мы будем игнорировать два других раздела кучи). Разделы памяти называются кучей малых объектов (SOH) и кучей больших объектов (LOH). SOH используется для хранения объектов меньше 85 Кб и LOH для всех остальных, более крупных объектов.

masterofsea147 20.11.19 в 03:31

0

Насколько большой объект?
Я открою этот раздел, заявив, что устройство памяти сложнее, чем мы можем рассказать в этой главе, но картина, которую я нарисую для вас, будет точной, насколько это будет возможно, и, что более важно, полезной, когда речь и идет о том чтобы помочь вам устранить проблемы связанные с памятью.

masterofsea147 20.11.19 в 03:42

0

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

masterofsea147 20.11.19 в 03:45

0

Рассмотрим следующий код:
Легко предположить, что размер MyClass при размещении в памяти включает в себя:
• 19 символов
• 86 000 байтов.

masterofsea147 20.11.19 в 03:47

0

Фактически, размер объекта будет складываться из размеров общего содержимого классов и памяти, необходимой для хранения указателей на строку и байтовый массив (переменные уровня класса), которые отдельно располагаются по кучам.

masterofsea147 20.11.19 в 03:58

0

Строка будет размещена на SOH, а ссылка на нее храниться в экземпляре класса; байтовый массив будет размещен на LOH, так как он больше 85 КБ.

masterofsea147 20.11.19 в 03:59

0

В 1 главе мы обсудили, где могут храниться ссылки на объекты которые расположены на одной из куч:
• другие объекты .NET
• стек
• регистры процессора
• статические поля
• очередь финализации(подробнее позже)
• неуправляемые Interop объекты.

masterofsea147 20.11.19 в 04:04

0

Объект должен быть доступным, чтобы принести пользу приложению. Для этого ему необходимо иметь ссылку, указывающую на него непосредственно из корневой ссылки (стек, статические поля, регистры ЦП, очередь финализации), или на него нужно ссылаться из объекта, который в конечном счете, сам имеет корневую ссылку.

masterofsea147 20.11.19 в 04:07

0

Все, что я хочу этим сказать - то, что, если проходясь по иерархии ссылок объекта, вы в конечном итоге достигаете корневой ссылки, тогда все объекты в этой иерархии доступны (rooted).

masterofsea147 20.11.19 в 04:12

0
+1

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

masterofsea147 20.11.19 в 04:15

0

Что такое автоматическая сборка мусора?
Говоря простыми словами, автоматическая сборка мусора - это выполнение модуля кода, который периодически запускается, производя поиск объектов, которые больше не используются приложением.

masterofsea147 20.11.19 в 04:20

0

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

masterofsea147 20.11.19 в 04:26

0

GC в .NET работает как для LOH, так и для SOH, но по-разному. С точки зрения сходства, каждая из куч может быть расширена путем добавления сегментов (областей памяти , которые запрашиваются у ОС) при необходимости.

masterofsea147 20.11.19 в 04:28

0

Когда запускается GC?
Тем не менее, GC пытается избежать этого путем освобождения пространства, где это возможно, удаляя неиспользуемые объектов, чтобы пространство можно было использовать повторно. Это гораздо эффективнее и позволяет избежать дорогостоящего расширения кучи.

masterofsea147 20.11.19 в 04:32

0

GC работает в отдельном потоке при достижении определенных условий памяти (которые мы обсудим через некоторое время) или когда приложение начинает исчерпывать память.

masterofsea147 20.11.19 в 04:34

0

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

masterofsea147 20.11.19 в 04:41

0

Если вы захотите поступить подобным образом, это в означает, что что-то пошло не так. Мой совет: сделайте глубокий вдох, затем возьмите хороший профилировщик памяти и найдите решение таким образом.

masterofsea147 20.11.19 в 04:44

0

Куча маленьких объектов(SOH)

masterofsea147 20.11.19 в 04:44

0

Выделение памяти и автоматическая сборка мусора в куче малых объектов (SOH) - это довольно сложный процесс.

masterofsea147 20.11.19 в 04:45

0

Поскольку большинство объектов, размещенных в приложении, меньше 85 Кб, SOH - интенсивно используемая область.

masterofsea147 20.11.19 в 04:46

0

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

masterofsea147 20.11.19 в 04:47

0

Чтобы помочь вам понять, для начала я собираюсь описать SOH на относительно высоком уровне, а затем постепенно дополнять, пока вы не получите полную картину. Доверьтесь мне, вы еще будете благодарны!

masterofsea147 20.11.19 в 04:49

0

Последовательное размещение на SOH

masterofsea147 20.11.19 в 04:50

0

В неуправляемых C/C++ приложениях объекты распределяются по куче везде где можно найти им место.

masterofsea147 20.11.19 в 04:51

0

Когда объект уничтожается программистом, пространство, которое этот объект использовал в куче, становится доступным для выделения других объектов.
на ней.

masterofsea147 20.11.19 в 04:54

0

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

masterofsea147 20.11.19 в 04:56

0

Другая проблема заключается в том, что всякий раз, когда происходит размещение объекта(что часто происходит), может потребоваться время, чтобы найти подходящую область в памяти для использования.

masterofsea147 20.11.19 в 04:57

0

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

masterofsea147 20.11.19 в 05:03

0

На рисунке 2.2 показано, как это работает. Вы можете увидеть ряд объектов, размещенных на SOH, а также ссылки, которые хранятся в стеке, в других объектах и в статических полях объектов-типов. На самом деле, все объекты, кроме объекта А, имеют корневые ссылки.

masterofsea147 20.11.19 в 05:11

0

Каждый из объектов расположен друг над другом, и вы можете видеть, что есть стрелка которая указывает на место, где будет размещен следующий объект; это так называемый указатель следующего объекта (NOP).

masterofsea147 20.11.19 в 05:36

0

Поскольку позиция NOP известна, при создании нового объекта он может быть мгновенно размещен в этом месте без дополнительных затрат на поиск места в Free Space таблицы, как это происходит в неуправляемых C/C++. приложениях

masterofsea147 20.11.19 в 05:47

0

Единственный недостаток этой картины в ее нынешнем виде - это потенциальная возможность памяти стать фрагментированной. Оглядываясь на рисунок 2.2, вы можете видеть, что у объекта А нет корневой ссылки, и он расположен в нижней части кучи. В результате это главный кандидат на сборку мусора, так как он больше не доступен для приложения, но это оставит пустое пространство между объектами в куче.

masterofsea147 20.11.19 в 06:02

0

Чтобы преодолеть проблему фрагментации, GC сжимает кучу (как упоминалось в предыдущей главе) и тем самым устраняет любые пробелы между объектами. Если GC очистит кучу на рисунке 2.2, она будет выглядеть как в примере на рисунке 2.3 после того, как GC завершит свою работу.

masterofsea147 20.11.19 в 06:05

0

Обратите внимание, что Объект A был удален, а его пространство занято другими объектами. В результате этого процесса фрагментация SOH резко уменьшается, хотя это все же может произойти, если объекты намеренно зафиксированы в памяти (подробнее об этом в Глава 3).

masterofsea147 20.11.19 в 06:21

0

Что еще используется?
В 1 главе мы концептуально рассмотрели тип алгоритмов, используемых GC для определения, какие из объектов все еще используются, а какие нет. Правило очень простое: «Если в конечном итоге у объекта нет корневой ссылки, то доступ к нему невозможен, поэтому он подлежит уничтожению. Оно, возможно, резкое, но очень эффективное.

masterofsea147 20.11.19 в 21:29

0

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

masterofsea147 06.12.19 в 01:52

0

Концептуально все, что я только что сказал, является правильным, но, как я уже упоминал в главе 1, фактический код GC выполняет обработку «используемого объекта» с отдельной фазой маркировки, фазой перемещения и фазой сжатия, что иногда бывает трудно выделить. Ясно, что алгоритм GC будет оптимизирован для максимально быстрого освобождения объектов трогая при этом как можно меньше объектов. Хотя это не всегда получается правильно, это контекст, в котором вы всегда должны видеть это.

masterofsea147 06.12.19 в 02:28

0

Оптимизация сборки мусора
Если вы подумаете об этом, потенциально есть небольшая проблема с созданием списков «все еще используемых объектов» и сжатием кучи, особенно если она очень большая.

masterofsea147 06.12.19 в 02:33

0

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

masterofsea147 06.12.19 в 03:00

0

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

masterofsea147 06.12.19 в 03:03

0

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

masterofsea147 06.12.19 в 03:08

0

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

masterofsea147 06.12.19 в 03:09

0

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

masterofsea147 06.12.19 в 03:10

0

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

masterofsea147 06.12.19 в 03:19

0

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

masterofsea147 06.12.19 в 03:21

0

Единственная небольшая проблема заключается в том, как они решили назвать коротко-, средне- и долгоживущие объекты: нулевое поколение, первое поколение и второе поколение, соответственно. Очевидно, что они не проконсультировались с отделом маркетинга!

masterofsea147 06.12.19 в 03:23

0

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

masterofsea147 06.12.19 в 03:24

0

Поколения сборки мусора
Вы помните схему, которую я показывал вам на рисунке 2.2?

masterofsea147 10.12.19 в 09:27

0
Минутку...

Следующая страница →