Нэш Трей - C# 2010. Ускоренный курс для профессионалов (2010) (1160865), страница 116
Текст из файла (страница 116)
ргссессеб ч1гспа1 чс1г( Ояярояе( Ьоо1 б1яроягпо ) ( 11( !саярояеб ) ( 11( саяразгпс ) ( // Здесь допускается использовать любые внутренние объекты. ГГ Этот класс, однако, их не имеет. ) г'г' Если используются объекты, о которых известно, Г/ что онн еще существуют, такие хях объекты, г'г' реализующие шаблон Ягп91есоп, важно убедиться, ГГ что онн являются безопасными з отношении потоков.
НеарОеясгоу( ГЬеНеяр )г ГЬеНеар = 1пГРГг.зего; б1ярсяеб = ггиег ) рпЬ1гс чо16 Огярояе() ( Ргярояе( Ггпе )г СС.Ясрргеязтгпя11ге( Гкая )г -Нзп32Неяр() ( Огярояе( Ра1яе )г ) рг1чаге 1пГРГг ГЬеНеярг рггчясе Ьоо1 б1ярояег( = Ря1яег ) Давайте проанализируем изменения, внесенные длл поддержки финализатора. Длл начала был добавлен финализатор с использованием знакомого синтаксиса деструкторах.
Также обратите внимание на добавление второго промежуточного уровня в реализацию Р1ярояе. Он необходим длл того, чтобы можно было знать, как произошло обращение к приватному методу Раярояе — через вызов Ргярояе или через фннализатор. Также в примере Раярозе (Ьоо1) реализован виртуально, тан что любой производный тип может просто переопределить этот метод длл модификации поведения освобождения.
Если класс И1п32Неар был бы помечен как яеа1еб, можно было бы изменить модификатор доступа метода с ргосессеб на рг1часе и убрать ключевое слово чггсця1. Как упоминалось ранее, надежно использовать подобьекты нельзя, если метод Раярояе был вызван из финализатора. На заметку! Некоторые предпочитают применять подход, основанный на том, что испольэовать объектные ссылки внутри метода Рг ерове, если Он был вызван финализатором, запрещено. Вообще говоря, нет причин, по которым нельзя было бы использовать эти объекты, если известно, что они существуют. Однако имейте в виду, что если финализатор был вызван в результате останова всего домена приложения, то объекты, которые вы считаете активными, уже могут не существовать. В действительности почти невозможно определить со 10099 уверенностью, действительна ли объектная ссылка в таких случаях.
Поэтому на стадии финализацин, по возможности, лучше просто не обращаться ни к каким ссылочным типам. Не забывайте, что финалнзатор — зто не деструктор! В поисках канонических форм Са 437 Метод Р1эрозе наносит удар по производительности — обратите внимание на вызов РС. г цргеззу1па11ке.
Финализатор этого объекта просто вызывает приватный метод Разрезе, и если вызван общедоступный метод Р1эрозе, поскольку пользователь забыл это сделать, то финализатор в дальнейшем вызываться не должен. Поэтому можно указать ОС, чтобы он не помещал экземпляр объекта в очередь финализации, когда вызван метод 1Р1эрозаЬ1е. Разрозе, Такая оптимизация более чем тривиальна, если учесть, что объекты, реализующие финализатор, существуют дольше, чем те. которые этого не делают. Когда ОС просматривает кучу в поисках мертвых объектов, которые нужно убрать, обычно он сжимает кучу и освобождает память.
Однако если объект оснащен финализатором, то вместо немедленного освобождения памяти ОС перемещает объект в список финалиэации, который обрабатывается отдельным потоком финализации. Это заставляет объект перемещаться к следующему поколению ОС, если только он уже не находится в высшем поколении. Как только поток финализации завершает свою работу над объектом, объект помечается к удалению, и ОС освобождает место во время следующего прохода. Таким образом, объект, реализующий финалиэатор, существует дольше, чем объект без финализатора. Если объект отнимает много памяти кучи, либо система создает много таких объектов, финализация становится существенным фактором.
Она не только снижает эффективность ОС, но также поглощает время процессора в потоке финализации. Вот почему по возможности следует подавлять финализацию внутри Разрозе. На заметку! Когда У объекта есть финализатор, он помещается во внутреннюю очередь СРВ для отслеживания этого факта, и ясно, что этот статус оказывает влияние на сс. яцрргеззг1па11зе. При нормальном выполнении, как упоминалось ранее, гарантировать доступность других объектных ссылок невозможно.
Однако во время останова приложения поток финалиэации действительно финализирует объекты иэ этой внутренней очереди финализации, и потому зти объекты доступны, и на них можно ссылаться в финализаторе. Определить, так ли это, можно с помо- щЬЮ Елтг1ГОПаЕПт . йаэз1тцтт1ОНПЯСаГСЕГ1 ИЛИ АррРОГВа1П. 1ЗГ1ла11Х1лдГОГРП1саб. Однако из того, что делать это можно, не следует, что зто должно делаться без тщательного обдумывания. Например, несмотря на то, что объект может быть достижимым, он может быть финализирован еще до обращения к нему.
Не удивляйтесь, если зто поведение изменится в будущих версиях СРН Теперь давайте внимательней рассмотрим влияние финализаторов на производительность ОС. Сборщик мусора СЬК реализован как учитывающий поколения (депегабопаЦ. Это значит, что выделенные объекты, которые относятся к старшим поколениям, существуют дольше, чем те, что относятся к младшим поколениям, и собираются менее часто, чем те. что относятся к поколению над ними. Описание тонких деталей алгоритма сборки мусора выходит за рамки настоящей книги.
Однако коснуться их на самом высоком уровне все же полезно. Например, ОС обычно пытается разместить новые объекты в поколении О. Кроме того, ОС предполагает, что объекты из поколения 0 будут существовать в течение относительно короткого времени. Поэтому, когда ОС пытается выделить место для объекта и видит, что кучу пора сжать, он освобождает место, удерживаемое мертвыми объектами поколения О, а объекты, которые еще существуют, во время этого сжатия перемещаются в поколение 1. На этой стадии, если ОС в состоянии найти достаточно места для распределения, он прекращает процесс сжатия кучи.
Он не будет пытаться сжимать поколение 1, если только ему не понадобится еще больше места, нли же он обнаружит, что куча поколения 1 полна и, вероятно, нуждается в сжатии. Затем при необходимости ОС проходит по всем поколениям. Во время этого прохода объект может быть перемещен только на один уровень. Поэтому, если объект перемещается из поколения 0 в поколение 1 во время сборки, и ОС должен по- 438 Глава 13 следовательно сжимать поколение 1 в том же проходе, то только что перемещенный объект остается в поколении 1.
В настоящее время куча СЬК состоит только из трех поколений. Потому, естественно. если объект находится в поколении 2, он не может быть перемещен в старшее поколение. С|К также содержит специальную кучу для выделения крупных объектов, которая в текущей версии содержит объекты, чей размер превышает 80 Кбайт. В будугцих версиях эта цифра вполне может измениться. Теперь рассмотрим, что происходит, когда объект из поколения 0 перемещается в поколение 1 во время сжатия кучи.
Даже если все корневые ссылки на обьект в поколении 1 находятся вне своих областей видимости, пространство может и не возвращаться в течение длительного времени, поскольку ОС не слишком часто сжимает область кучи поколения 1. Объекты, реализующие финализаторы, во время прохода ОС попадают в так называемую Р-достижимую (6еасЬаЫе) очередь финализации. Эта ссьика в Р-достижимой очереди считается корневой ссылкой. Поэтому объект будет перемещен в поколение 1, если он в данный момент находится в поколении О. Однако уже известно, что объект прекращает существование. Фактически, как только г-достижимая очередь пустеет, это означает, что объект, скорее всего, будет мертв, если только он не восстановится во время процесса финализации.
Вот тут-то и загвоздка. Этот объект с финализатором прекращает сущеспюваиие, но поскольку он был помещен в г-достижимую очередь и потому перемещен в более старшее поколение, весьма вероятно, что он останется где-то в ОС до тех пор, пока не случится сжатие старшего поколения.
По атой причине важно реализовывать финализаторы только при необходимости. Обычно это означает реализацию финализатора только в том случае, если объект непосредственно содержит неуправляемые ресурсы. Например, рассмотрим тип Бузгеи. 10. Б11езггеаж через который осуществляется управление файлами операционной системы. Е11еБСгези содержит дескриптор неуправляемого реСУРса, а именно — дескриптор файла операционной системы, и потому должен иметь финализатор для ситуаций, когда разработчик забудет вызвать Рьзрозе или 01озе на экземпляре Б11еБСгеат. При реализации типа, содержащего одиночный экземпляр 811езггеат, необходимо учитывать следующие моменты.















