Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
40.doc
Скачиваний:
14
Добавлен:
30.04.2022
Размер:
646.66 Кб
Скачать

11.2. Сборки

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

Сборка состоит из четырех разделов. Первый раздел представляет собой декларацию сборки. Декларация содержит сведения о самой сборке. К этой информации относится, в частности, имя сборки, номер ее версии, сведения о соответствии типов и параметры культурной среды (язык и региональные стандарты). Второй раздел сборки содержит метаданные типов, т.е. сведения о типах данных, используемых в программе. Среди прочих преимуществ метаданные типов способствуют межъязыковой возможности взаимодействия. Третий раздел сборки содержит программный код в формате MSIL (Microsoft Intermediate Language — промежуточный язык корпорации Microsoft). И четвертый раздел сборки содержит ресурсы, используемые программой.

Исполняемый файл, создаваемый во время компиляции программы на С#, представляет собой сборку, содержащую исполняемый код этой программы, а также другие виды информации. Таким образом, когда компилируется программа на С#, сборка получается автоматически.

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

Помимо указания пространства имен с помощью поддерживаемого в С# ключевого слова using, компилятору С# необходимо сообщить имя сборки, в которой содержится само CIL-oпpeделение упоминаемого типа. Многие из ключевых пространств имен .NET находятся внутри сборки mscorlib.dll.

Подавляющее большинство сборок в .NET Framework размещено в специально предназначенном для этого каталоге, который называется глобальным кэшем сборок (Global Assembly Cache — GAC). На машине Windows по умолчанию GAC может располагаться внутри каталога %windir%\assembly

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

12. Время жизни переменных и область видимости переменных. Оператор new и сборка мусора

Программистам на С# никогда не приходится непосредственно удалять управляемый объект из памяти (в языке С# нет даже ключевого слова вроде delete). Вместо этого объекты .NET размещаются в области памяти, которая называется управляемой кучей (managed heap), откуда они автоматически удаляются сборщиком мусора, когда наступает "определенный момент в будущем".

Для начала вспомним, что класс представляет собой схему, которая описывает то, каким образом экземпляр данного типа должен выглядеть и вести себя в памяти. Определяются классы в файлах кода (которым по соглашению назначается расширение *.cs). Как только класс определен, с использованием ключевого слова new, поддерживаемого в С#, можно размещать в памяти любое количество его объектов. Однако при этом следует помнить, что ключевое слово new возвращает ссылку на объект в куче, а не фактический объект. Если ссылочная переменная объявляется как локальная переменная в контексте метода, она сохраняется в стеке для дальнейшего использования в приложении. Для вызова членов объекта к сохраненной ссылке должна применяться операция точки С#.

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

Ссылки на объекты в управляемой куче

Рис. 4. Отношения между классами объектами и ссылками

При создании приложений на С# можно смело полагать, что исполняющая среда .NET будет сама заботиться об управляемой куче без непосредственного вмешательства со стороны программиста. На самом деле "золотое правило" по управлению памятью в .NET звучит просто: «Размещайте объект в управляемой куче с использованием ключевого слова new и забывайте об этом.»

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

Прежде чем ознакомиться с точными правилами, которые определяют момент, когда объект должен удаляться из управляемой кучи, нужно рассмотрим роль CIL-инструкции newobj, которая вставляется компилятором C# в реализацию метода при обнаружении ключевого слова new. Управляемая куча представляет собой нечто большее, чем просто случайный фрагмент памяти, к которому CLR получает доступ. Сборщик мусора .NET "убирает" кучу довольно тщательно, причем (при необходимости) даже сжимает пустые блоки памяти с целью оптимизации. Чтобы ему было легче это делать, в управляемой куче поддерживается указатель (обычно называемый указателем на следующий объект или указателем на новый объект), который показывает, где точно будет размещаться следующий объект.

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

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

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

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

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

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

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

Чтобы увидеть все это на примере, предположим, что в управляемой куче содержится набор объектов с именами A, B,C,D,E,F,G. Во время сборки мусора эти объекты (а также любые внутренние объектные ссылки, которые они могут содержать) будут исследованы на предмет наличия у них активных корневых элементов. После построения графа все недостижимые объекты (которыми в примере пусть будут объекты С и F) помечаются как являющиеся мусором. После того как объект помечен для уничтожения (в данном случае это объекты С и F, поскольку в графе объектов они во внимание не принимаются), они будут удалены из памяти. Оставшееся пространство в куче будет после этого сжиматься до компактного состояния, что, в свою очередь, вынудит CLR изменить набор активных корневых элементов приложения (и лежащих в их основе указателей) так, чтобы они ссылались на правильное место в памяти (это делается автоматически и прозрачно). И, наконец, указатель на следующий объект тоже будет подстраиваться так, чтобы указывать на следующий доступный участок памяти.

При попытке обнаружить недостижимые объекты CLR-среда не проверяет буквально каждый находящийся в куче объект. Для оптимизации процесса каждый объект в куче относится к определенному "поколению". Смысл в применении поколений выглядит довольно просто: чем дольше объект находится в куче, тем выше вероятность того, что он там и будет оставаться. Например, класс, определенный в главном окне настольного приложения, будет оставаться в памяти вплоть до завершения выполнения программы. С другой стороны, объекты, которые были размещены в куче лишь недавно (как, например, те, что находятся в пределах области действия метода), вероятнее всего будут становиться недостижимым довольно быстро. Исходя из этих предположений, каждый объект в куче относится к одному из перечисленных ниже поколений.

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

• Поколение 1. Идентифицирует объект, который уже "пережил" один процесс сборки мусора (был помечен как подлежащий удалению в процессе сборки мусора, но не был удален из-за наличия достаточного места в куче).

• Поколение 2. Идентифицирует объект, которому удалось пережить более одного прогона сборщика мусора.

Сборщик мусора сначала анализирует все объекты, которые относятся к поколению 0. Если после их удаления остается достаточное количество памяти, статус всех остальных (уцелевших) объектов повышается до поколения 1.

Если все объекты поколения 0 уже были проверены, но все равно требуется дополнительное пространство, проверяться на предмет достижимости и подвергаться процессу сборки мусора начинают объекты поколения 1. Объектам поколения 1, которым удалось уцелеть после этого процесса, затем назначается статус объектов поколения 2.

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

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

12.1. Тип System.CG

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

Ниже приведены краткие описания некоторых наиболее интересных членов класса System.GC:

1) AddMemorуPressure(), RemoveMemoryPressure() - gозволяют указывать числовое значение, отражающее "уровень срочности", который вызывающий объект применяет в отношении к сборке мусора. Следует иметь в виду, что эти методы должны изменять уровень давления в тандеме и, следовательно, никогда не устранять больше давления, чем было добавлено.

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

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

4) GetGeneration() - возвращает информацию о том, к какому поколению в настоящий момент относится объект.

5) GetTotalMemory() - возвращает информацию о том, какой объем памяти (в байтах) в настоящий момент занят в управляемой куче. Булевский параметр указывает, должен ли вызов сначала дождаться выполнения сборки мусора, прежде чем возвращать результат.

6) MaxGeneration - возвращает информацию о том, сколько максимум поколений поддерживается в целевой системе.

7) SuppressFinalize() - позволяет устанавливать флаг, указывающий, что для данного объекта не должен вызываться его метод Finalize()

8) WaitForPendingFinalizers() - позволяет приостанавливать выполнение текущего потока до тех пор, пока не будут финализированы все объекты, предусматривающие финализацию. Обычно вызывается сразу же после вызова метода GC.Collect()

Сборщик мусора .NET предназначен в основном для того, чтобы управлять памятью вместо разработчиков. Однако в очень редких случаях требуется принудительно запустить сборку мусора с помощью метода GC.Collect(). Примеры таких ситуаций приведены ниже:

• Приложение приступает к выполнению блока кода, прерывание которого возможным процессом сборки мусора является недопустимым.

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

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

static void Main(string [ ] args)

{

// Принудительная активизация процесса сборки мусора и

// ожидание завершения финализации каждого из объектов.

GC.Collect();

GC.WaitForPendingFinalizers();

}

В случае принудительной активизации сборки мусора нужно также не забывать вызвать метод GC.WaitForPendingFinalizers(). Это дает возможность всем финализируемым объектам произвести любую необходимую очистку перед продолжением работы программы. Метод GC.WaitForPendingFinalizers() незаметно приостанавливает выполнение вызывающего "потока" во время процесса сборки мусора, что очень хорошо, поскольку исключает вероятность вызова в коде каких-либо методов на объекте, который в текущий момент уничтожается.

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

Например, чтобы CLR-среда анализировала только объекты поколения 0, необходимо использовать следующий код:

static void Main(string [ ] args)

{

// Исследование только объектов поколения 0.

GC.Collect(0);

GC.WaitForPendingFinalizers ();

}

Вдобавок методу Collect() во втором параметре может передаваться значение перечисления GCCollectionMode, которое позволяет более точно указать, каким образом исполняющая среда должна принудительно инициировать сборку мусора. Ниже показаны значения, доступные в этом перечислении:

public enum GCCollectionMode

{

Default, // Текущим значением по умолчанию является Forced.

Forced, // Указывает исполняющей среде начать сборку мусора //немедленно

Optimized // Позволяет исполняющей среде выяснить, оптимален //ли настоящий момент для удаления объектов.

}

Как и при любой сборке мусора, в случае вызова GC.Collect() уцелевшим объектам назначается статус объектов более высокого поколения.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]