Under the Hood of .NET Memory Management / Под капотом у памяти в .Net: Chapter 2: The Simple Heap Model
| Английский оригинал | Перевод на русский | |
|---|---|---|
Сожалеем, но текст оригинала доступен только зарегистрированным пользователям. |
В главе 1 мы увидели как стек используется для хранения типов значений и указателей на объекты ссылочных типов, которые хранятся в куче. Это позволяет каждому вызванному методу сохранять состояние локальных переменных между вызовами других методов. Мы также обсудили устройство самой кучи и как происходит создание, выделение памяти и уничтожение объектов управляемых .NET во время исполнения. masterofsea147 20.11.19 в 03:12 |
|
|
Чтобы действительно понять, как .NET Framework управляет памятью нам нужно более детально изучить как производится выделение памяти в куче. masterofsea147 20.11.19 в 03:17 |
||
|
Управляемые кучи masterofsea147 20.11.19 в 03:31 |
||
|
Насколько большой объект? masterofsea147 20.11.19 в 03:42 |
||
|
Заманчиво предположить, что размер объекта в куче складывается из того, что в нем содержится. На самом деле, он не включает объекты, которые он, в свою очередь, содержит, так как они располагаются отдельно. masterofsea147 20.11.19 в 03:45 |
||
|
Рассмотрим следующий код: masterofsea147 20.11.19 в 03:47 |
||
|
Фактически, размер объекта будет складываться из размеров общего содержимого классов и памяти, необходимой для хранения указателей на строку и байтовый массив (переменные уровня класса), которые отдельно располагаются по кучам. masterofsea147 20.11.19 в 03:58 |
||
|
Строка будет размещена на SOH, а ссылка на нее храниться в экземпляре класса; байтовый массив будет размещен на LOH, так как он больше 85 КБ. masterofsea147 20.11.19 в 03:59 |
||
|
В 1 главе мы обсудили, где могут храниться ссылки на объекты которые расположены на одной из куч: masterofsea147 20.11.19 в 04:04 |
||
|
Объект должен быть доступным, чтобы принести пользу приложению. Для этого ему необходимо иметь ссылку, указывающую на него непосредственно из корневой ссылки (стек, статические поля, регистры ЦП, очередь финализации), или на него нужно ссылаться из объекта, который в конечном счете, сам имеет корневую ссылку. masterofsea147 20.11.19 в 04:07 |
||
|
Все, что я хочу этим сказать - то, что, если проходясь по иерархии ссылок объекта, вы в конечном итоге достигаете корневой ссылки, тогда все объекты в этой иерархии доступны (rooted). masterofsea147 20.11.19 в 04:12 |
+1 | |
|
Этот простой принцип является краеугольным камнем того, как работает управление памятью .NET. Правило, что объект, который больше не доступен, может быть очищен, делает автоматическую сборку мусора возможной. masterofsea147 20.11.19 в 04:15 |
||
|
Что такое автоматическая сборка мусора? masterofsea147 20.11.19 в 04:20 |
||
|
Это освобождает разработчиков от ответственности за явное уничтожение созданных ими объектов, решая проблемы забытых объектов вызывающие утечки памяти. masterofsea147 20.11.19 в 04:26 |
||
|
GC в .NET работает как для LOH, так и для SOH, но по-разному. С точки зрения сходства, каждая из куч может быть расширена путем добавления сегментов (областей памяти , которые запрашиваются у ОС) при необходимости. masterofsea147 20.11.19 в 04:28 |
||
|
Когда запускается GC? masterofsea147 20.11.19 в 04:32 |
||
|
GC работает в отдельном потоке при достижении определенных условий памяти (которые мы обсудим через некоторое время) или когда приложение начинает исчерпывать память. masterofsea147 20.11.19 в 04:34 |
||
|
Разработчик также может явно заставить GC работать, используя следующую строку кода: masterofsea147 20.11.19 в 04:41 |
||
|
Если вы захотите поступить подобным образом, это в означает, что что-то пошло не так. Мой совет: сделайте глубокий вдох, затем возьмите хороший профилировщик памяти и найдите решение таким образом. masterofsea147 20.11.19 в 04:44 |
||
|
Куча маленьких объектов(SOH) masterofsea147 20.11.19 в 04:44 |
||
|
Выделение памяти и автоматическая сборка мусора в куче малых объектов (SOH) - это довольно сложный процесс. masterofsea147 20.11.19 в 04:45 |
||
|
Поскольку большинство объектов, размещенных в приложении, меньше 85 Кб, SOH - интенсивно используемая область. masterofsea147 20.11.19 в 04:46 |
||
|
Таким образом, чтобы максимизировать производительность, были добавлены различные оптимизации, которые, к сожалению, увеличивают сложность. masterofsea147 20.11.19 в 04:47 |
||
|
Чтобы помочь вам понять, для начала я собираюсь описать SOH на относительно высоком уровне, а затем постепенно дополнять, пока вы не получите полную картину. Доверьтесь мне, вы еще будете благодарны! masterofsea147 20.11.19 в 04:49 |
||
|
Последовательное размещение на SOH masterofsea147 20.11.19 в 04:50 |
||
|
В неуправляемых C/C++ приложениях объекты распределяются по куче везде где можно найти им место. masterofsea147 20.11.19 в 04:51 |
||
|
Когда объект уничтожается программистом, пространство, которое этот объект использовал в куче, становится доступным для выделения других объектов. masterofsea147 20.11.19 в 04:54 |
||
|
Проблема в том, что со временем куча может стать фрагментированной, с небольшими пространствами, которые обычно не достаточно велики для эффективного использования. В результате куча становится masterofsea147 20.11.19 в 04:56 |
||
|
Другая проблема заключается в том, что всякий раз, когда происходит размещение объекта(что часто происходит), может потребоваться время, чтобы найти подходящую область в памяти для использования. masterofsea147 20.11.19 в 04:57 |
||
|
Чтобы минимизировать время выделения и практически исключить фрагментацию кучи .NET выделяет объекты последовательно, один поверх другого, и отслеживает, где выделить следующий объект masterofsea147 20.11.19 в 05:03 |
||
|
На рисунке 2.2 показано, как это работает. Вы можете увидеть ряд объектов, размещенных на SOH, а также ссылки, которые хранятся в стеке, в других объектах и в статических полях объектов-типов. На самом деле, все объекты, кроме объекта А, имеют корневые ссылки. masterofsea147 20.11.19 в 05:11 |
||
|
Каждый из объектов расположен друг над другом, и вы можете видеть, что есть стрелка которая указывает на место, где будет размещен следующий объект; это так называемый указатель следующего объекта (NOP). masterofsea147 20.11.19 в 05:36 |
||
|
Поскольку позиция NOP известна, при создании нового объекта он может быть мгновенно размещен в этом месте без дополнительных затрат на поиск места в Free Space таблицы, как это происходит в неуправляемых C/C++. приложениях masterofsea147 20.11.19 в 05:47 |
||
|
Единственный недостаток этой картины в ее нынешнем виде - это потенциальная возможность памяти стать фрагментированной. Оглядываясь на рисунок 2.2, вы можете видеть, что у объекта А нет корневой ссылки, и он расположен в нижней части кучи. В результате это главный кандидат на сборку мусора, так как он больше не доступен для приложения, но это оставит пустое пространство между объектами в куче. masterofsea147 20.11.19 в 06:02 |
||
|
Чтобы преодолеть проблему фрагментации, GC сжимает кучу (как упоминалось в предыдущей главе) и тем самым устраняет любые пробелы между объектами. Если GC очистит кучу на рисунке 2.2, она будет выглядеть как в примере на рисунке 2.3 после того, как GC завершит свою работу. masterofsea147 20.11.19 в 06:05 |
||
|
Обратите внимание, что Объект A был удален, а его пространство занято другими объектами. В результате этого процесса фрагментация SOH резко уменьшается, хотя это все же может произойти, если объекты намеренно зафиксированы в памяти (подробнее об этом в Глава 3). masterofsea147 20.11.19 в 06:21 |
||
|
Что еще используется? masterofsea147 20.11.19 в 21:29 |
||
|
Когда запускается GC, он получает список всех корневых объектов и для каждого рекурсивно просматривает ссылочные объекты, которые расположены в иерархии этого корня, и все объекты, которые они содержат. masterofsea147 06.12.19 в 01:52 |
||
|
Концептуально все, что я только что сказал, является правильным, но, как я уже упоминал в главе 1, фактический код GC выполняет обработку «используемого объекта» с отдельной фазой маркировки, фазой перемещения и фазой сжатия, что иногда бывает трудно выделить. Ясно, что алгоритм GC будет оптимизирован для максимально быстрого освобождения объектов трогая при этом как можно меньше объектов. Хотя это не всегда получается правильно, это контекст, в котором вы всегда должны видеть это. masterofsea147 06.12.19 в 02:28 |
||
|
Оптимизация сборки мусора masterofsea147 06.12.19 в 02:33 |
||
|
Навигация по огромным графам объектов и копирование множества живых объектов поверх мертвых займет значительное количество времени. Чтобы получить максимальную производительность, весь этот процесс должен быть оптимизирован, и это именно то, что сделали ребята из .NET. masterofsea147 06.12.19 в 03:00 |
||
|
Они решили классифицировать все объекты в одну из трех групп. С одной стороны, у вас есть недолговечные объекты, которые быстро попадают в память, используются и удаляются. masterofsea147 06.12.19 в 03:03 |
||
|
На другом конце спектра у вас есть долгоживущие объекты, которые создаются на ранней стадии, а затем остаются в использовании в течение неопределенного времени. В-третьих, и, наконец, у вас, как вы уже догадались, есть среднесрочные объекты, которые где-то посередине. masterofsea147 06.12.19 в 03:08 |
||
|
Анализируя типичные схемы аллокации, они обнаружили, что недолговечные объекты встречаются гораздо чаще, чем долгоживущие. masterofsea147 06.12.19 в 03:09 |
||
|
Они также поняли, что наиболее вероятными претендентами на сборку мусора были недавно выделенные объекты. masterofsea147 06.12.19 в 03:10 |
||
|
Поскольку большинство объектов создаются внутри вызова метода и выходят из области видимости после завершения метода, они по определению, не могут долго существовать. Таким образом была разработана действительно классная оптимизация, которая заключалась в том, чтобы осматривать и собирать недавно выделенные объекты чаще, чем старые. masterofsea147 06.12.19 в 03:19 |
||
|
Это был простой и элегантный подход, который уменьшил количество корневых ссылок, подлежащих проверке, и количество иерархий объектов, по которым нужно перемещаться. Это также уменьшило количество требуемого уплотнения кучи. masterofsea147 06.12.19 в 03:21 |
||
|
Единственная небольшая проблема заключается в том, как они решили назвать коротко-, средне- и долгоживущие объекты: нулевое поколение, первое поколение и второе поколение, соответственно. Очевидно, что они не проконсультировались с отделом маркетинга! masterofsea147 06.12.19 в 03:23 |
||
|
Несмотря на то, что им не хватает описательных качеств, эти термины широко известны и понятны, и я буду (только слегка неохотно) использовать их отныне, так как я не хочу путать вас моими личными предпочтениями. masterofsea147 06.12.19 в 03:24 |
||
|
Поколения сборки мусора masterofsea147 10.12.19 в 09:27 |
