.NET Internals. Memory Structure
Дмитрий БахтенковСтруктура памяти ОС. Основы
Вы когда-нибудь задумывались о том, что скрывается под капотом приложений, которые вы разрабатываете?
Вы когда-нибудь удивлялись, что после окончания университета не нужно беспокоиться о выделении и освобождении памяти с использованием языков программирования высокого уровня, таких как Java или C#?
Все еще помните времена C++ с оператором delete?
Этим постом я хотел бы представить новую серию статей “.NET Internals”.
Содержание:
- CLR
- Структура памяти ОС
- Разделение памяти между процессами
CLR
Каждое .net приложение управляется с помощью CLR (Common language runtime), которая является частью .net framework, который должен быть установлен на компьютер, где .net-приложение запускается. Исключением являются .NET Core self-contained приложения, которые не будут обсуждаться в этой серии статей. По-умолчанию, CLR запускается для каждого .net-процесса как внутренний процесс, то есть как часть приложения .net
CLR отвечает за многие вещи, пока ваше приложение запущено:
- Выполнение CIL (Common Intermediate Language)
- Обеспечение безопасности типов
- Обработка исключений
- Управление потоками
- Сборка мусора
Структура памяти ОС
Уже запущенное приложение является процессом в операционной системе. Как разработчики, мы знаем, что такой процесс сохраняет некоторые данные в оперативную память, которая определяется доступным кол-вом ОЗУ на ПК. Однако, процесс никогда не взаимодействует с этой "открытой памятью" - только ОС имеет прямой доступ к ней. Когда CLR запускается внутри .net-процесса, она запрашивает некоторое количество памяти у ОС. Эта выделенная память называется "виртуальным адресным пространством". Каждый процесс имеет своё виртуальное адресное пространство, но все процессы запущены на выделенной ПК физической оперативной памяти.
64-bit OS
64-битная Windows, теоретически, может иметь виртуальное адресное пространство размером в 16 экзобайт (2^64 байта).
В реальности, используется только его часть: 8-ми терабайтный блок используется пользователем, а части 248-терабайт используются операционной системой

Разделение памяти между процессами
Вся виртуальная память распределяется между процессами в системе. Как было сказано выше, множество процессов используют одну и ту же память, но имеют доступ к разным её частям.
Можно сказать, каждый процесс резервирует различные страницы памяти, когда это необходимо. Несмотря на то, что 2 ГБ виртуальной памяти по умолчанию, зарезервированные для одного процесса, могут показаться большим количеством, каждый разработчик должен знать, что при выделении (резервировании блока памяти) (вручную или системой управления памятью) менеджеру виртуальной памяти необходимо найти один непрерывный блок виртуальной памяти, достаточно большой для хранения запрошенного объема данных. Это правило приводит к наличию “дыр” в памяти.

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