Under the Hood of .NET Memory Management / Под капотом у памяти в .Net: Section 1: Introduction to .NET Memory Management
| Английский оригинал | Перевод на русский | |
|---|---|---|
Сожалеем, но текст оригинала доступен только зарегистрированным пользователям. |
Глава 1: Введение. quandisti 20.11.19 в 02:30 |
+1 |
|
Недостатком этого подхода является то, что вы можете невероятно хорошо знать синтаксис языка и полезные классы фреймворка, но практически не иметь представления о влиянии того, что вы пишете, на производительность и память. Эти "пробелы в знаниях" обычно всплывают под конец процесса разработки проекта, при нагрузочном тестировании, или даже когда продукт уже используется пользователями и возникла какая-то проблема. quandisti 20.11.19 в 02:33 |
||
|
Существуют тысячи приложений, написанных разработчиками такой квалификации. Если вы читаете эту книгу, то, вероятно, вы - один из них; поэтому, чтобы закрыть пробелы в ваших знаниях, мы отправимся в путешествие к глубинам управления памятью в среде исполнения .NET. quandisti 20.11.19 в 02:36 |
||
|
Вы узнаете, как приложения на самом деле работают под капотом. Когда вы поймете основы управления памятью, вы станете писать более быстрый, более эффективный -- лучше, чем раньше -- код, и будете намного лучше подготовлены к ситуации, когда в одном из ваших приложений возникнет какая-то проблема. quandisti 20.11.19 в 02:39 |
||
|
Итак, для того чтобы все прошло гладко, мы будем потихоньку развивать свои знания с нуля. Вместо того, чтобы сразу переходить к сложным определениям и деталям реализации, я собираюсь начать с упрощенной модели и добавлять сложность по мере прочитанного материала, от главы к главе. При таком подходе к концу книги вы будете иметь уверенное понимание всех тем которые будут рассказаны. masterofsea147 16.12.19 в 07:34 |
||
|
На самом деле, к концу книги мы рассмотрим управление памятью вплоть до уровня операционной системы, но мы будем делать это по одному шагу за раз. Как говорится, «путешествие в тысячу миль начинается с одного шага». masterofsea147 16.12.19 в 07:37 |
||
|
Беглый обзор quandisti 20.11.19 в 10:17 |
+1 | |
|
Все, что находится в куче, имеет адрес; эти адреса используются для отслеживания выполнения программы и изменений состояния приложения. quandisti 20.11.19 в 10:18 |
||
|
Обычно приложения инкапсулируют код в методы и классы, поэтому .NET необходимо отслеживать цепочки вызовов методов, и состояние данных, хранимое в каждом из этих вызовов методов. quandisti 20.11.19 в 10:24 |
||
|
К тому же, когда один метод вызывает другой, состояние вызывающего метода(переменные, параметры) сохраняется, пока вызванный метод выполняется. Когда выполнение вызванного метода завершается, оригинальное состояние вызвавшего должно быть восстановлено, чтобы он мог продолжить выполнение. quandisti 20.11.19 в 10:26 |
||
|
Чтобы за всем следить (и этого "всего" часто довольно много), .NET поддерживает специальную структуру данных -- стек, который используется для отслеживания состояния выполнения потока и всех сделанных вызовов методов. quandisti 20.11.19 в 10:28 |
||
|
Стек quandisti 21.11.19 в 03:16 |
||
|
Используя этот простой механизм, состояние каждого метода сохраняется между вызовами других методов, и все они хранятся отдельно друг от друга. masterofsea147 16.12.19 в 07:49 |
||
|
В листинге 1.1 Method1 вызывает Method2, передавая в качестве параметра значение типа int. Чтобы вызвать Method2, поток приложения должен сначала записать адрес возврата, который является следующей строкой кода после вызова Method2. Когда Method2 завершает выполнение, этот адрес используется для вызова следующей строки кода в Method1(строка 4). Адрес возврата, следовательно, помещается в стек. masterofsea147 16.12.19 в 10:27 |
||
|
Параметры, передаваемые в Method2, также помещаются в стек. Наконец, мы готовы перейти к выполнению по адресу кода метода Method2. masterofsea147 16.12.19 в 10:48 |
||
|
Если мы поместим точку останова на строке 13, стек будет выглядеть примерно так, как показано на рисунке 1.1. Очевидно, это огромное упрощение, и в качестве адресов возврата не будут использоваться номера строк кода, но, надеюсь, вы поняли идею. masterofsea147 20.12.19 в 10:35 |
||
|
На рисунке 1.1 стековые кадры для методов 1, 2 и 3 размещены друг над другом, и в настоящий момент текущий стековый кадр принадлежит методу Method3, который является текущим исполняемым методом. Когда Method3 завершает выполнение, его кадр стека удаляется, точка выполнения программы перемещается обратно в Method2 (строка 9 в листинге 1), и выполнение продолжается. masterofsea147 20.12.19 в 10:40 |
||
|
Хорошее и простое решение сложной задачи, но не забывайте - что, если ваше приложение имеет несколько потоков, то каждый поток будет иметь свой собственный стек. masterofsea147 16.12.19 в 10:50 |
||
|
Итак, где же находится куча данных? В стеке могут храниться переменные, которые представляют собой примитивные типы данных, определенные в .NET. К ним относятся следующие типы (см.http://tinyurl.com/FrameworkOverview): Примитивные типы данных, часть общей системы типов (CTS), изначально понятные всем компиляторам языка .NET, а так же все экземпляры типов значений. Любой экземпляр этих типов данных обычно хранится в стеке. masterofsea147 20.12.19 в 10:46 |
||
|
С другой стороны, экземпляры ссылочных типов, в том числе: masterofsea147 20.12.19 в 10:48 |
||
|
Когда создается экземпляр ссылочного типа, в стеке сохраняется только ссылка на объект. Сам фактический экземпляр создается в куче, а его адрес хранится в стеке. masterofsea147 20.12.19 в 10:49 |
||
|
Рассмотрим следующий код: masterofsea147 20.12.19 в 10:52 |
||
|
Позже мы увидим, как это влияет на управление памятью и сборщик мусора. masterofsea147 20.12.19 в 10:53 |
||
|
Более подробно о типах значений и ссылочных типах masterofsea147 20.12.19 в 10:56 |
+1 | |
|
Рассмотрим код в листинге 1.3. masterofsea147 20.12.19 в 10:58 |
||
|
В стеке расположены два целых числа с одинаковым значением. Обратите внимание, что есть две переменные стека, v1 и v2, и все, что было сделано, - присвоение им одинакового значения. masterofsea147 20.12.19 в 11:00 |
||
|
Давайте посмотрим на похожий сценарий, на этот раз использующий класс, который я определил как MyClass, который (очевидно) является ссылочным типом: размещение точки останова в строке 5 в листинге 1.4 приведет к тому, что два экземпляра класса MyClass будут размещены в куче. masterofsea147 20.12.19 в 11:04 |
||
|
Давайте теперь поговорим на ту тему, о которой вас всегда спрашивают на собеседовании - упаковке и распаковке. На самом деле это действительно легко понять, и просто заключается в дополнительной работе, которая требуется, когда ваш код размещает экземпляры типов значений (например, int, char и т. д.) в куче, а не на стеке. Как мы видели ранее, размещение в куче более затратно и, следовательно, менее эффективно. masterofsea147 20.12.19 в 11:09 |
||
|
Классический пример упаковки и распаковки выглядит примерно так: masterofsea147 20.12.19 в 11:40 |
||
|
Вопрос, который смущает всех - зачем вам это делать ведь это не имеет никакого смысла. Ответ на этот вопрос заключается в том, что вы можете произвести упаковку типов значений, даже не подозревая об этом. masterofsea147 20.12.19 в 11:44 |
||
|
В листинге 1.10 показано, как может происходить незаметная упаковка и распаковка, и я уверен, что в какой-то момент вы писали похожий код. Добавление целого числа (тип значения) к ArrayList приведет к выполнению операции упаковки, так как, чтобы создать динамический массив для любых типов, метод Add принимает object в качестве параметра. Итак, чтобы добавить целое число в ArrayList, оно должен быть размещено в куче. masterofsea147 20.12.19 в 11:48 |
||
|
При обращении к целому числу в строке 6 создается новая переменная стека "p", и ее значение устанавливается равным первому целому числу в ArrayList. masterofsea147 20.12.19 в 11:49 |
||
|
Короче говоря, выполняется намного больше работы, чем необходимо, и если бы вы делали это в цикле с тысячами целых чисел, производительность была бы значительно ниже. masterofsea147 20.12.19 в 11:50 |
||
|
Еще кое-что про кучу quandisti 21.11.19 в 03:16 |
||
|
Сборка мусора quandisti 21.11.19 в 03:16 |
||
|
Все вместе они называются корневыми ссылками. masterofsea147 20.12.19 в 11:51 |
||
