Страницы

Поиск по вопросам

Показаны сообщения с ярлыком сборщик-мусора. Показать все сообщения
Показаны сообщения с ярлыком сборщик-мусора. Показать все сообщения

четверг, 9 апреля 2020 г.

Гибридное управление памятью

#память #любой_язык #сборщик_мусора

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

Иными словами, нужны две отдельные кучи для работы с памятью. 
    


Ответы

Ответ 1



Скорее всего вам подойдёт C++/CLI. Это Microsoft'овский гибрид C++ и платформы .NET. В нём .NET-объекты создаются при помощи gcnew и управляются сборщиком мусора, а стандартные C++-объекты создаются при помощи new и удаляются вручную через delete.

вторник, 17 марта 2020 г.

Возможность перевести GC в режим реального времени

#c_sharp #сборщик_мусора #производительность


Недавно увидел что на хабре промелькнула информация о том, что

..в C# есть возможность перевести GC в
режим реального времени (возможность
гарантировать выполнение кода
последовательно без перерыва на сборку
мусора).

может поподробней кто нибудь пожалуйста объяснить как это сделать?    


Ответы

Ответ 1



В пространстве имен System.Runtime имеется статический класс GCSettings, у которого есть свойство LatencyMode, определяющее уровень вмешательства сборщика в работу вашего приложения при выполнении сборки мусора. Подробнее о режимах, регулируемых этим свойством, можно прочитать в этой статье

вторник, 28 января 2020 г.

Почему java программа не освобождает память?

#java #память #jvm #сборщик_мусора


У меня тут творятся очень странные вещи с памятью. Есть главный класс в котором main
метод запускает множество потоков. Эти созданные потоки через какое то время убиваю,никаких
объектов и потоков но память выросла и не освобождается..наблюдаю за всем этим в профайлере(JProfiler).
Нет никаких потоков и обьектов кроме main, но память все так же стоит и не освобождается,
когда запускаю новые потоки память растет с того же места на котором остановилось до
этого. т.е. похож на утечку памяти. но вроде никаких утечек нет. В памяти висят не
очень много обьектов,самый большой это массив char[] Посмотрите на картинках: в диспетчере
задач показывает что программа съедает 259 мб.

и на данный момент нет ничего кроме метода main который в состоянии Thread.sleep().



ниже объекты которые в памяти :



И вот общие сведения о том сколько жрет моя программа : 


Объясните, что тут к чему? Почему если в памяти нет почти никаких объектов  приложение
занимает 81 мб(это то что написано в профайлере). Как будто приложение распухло но
внутри ничего нет.
    


Ответы

Ответ 1



У Java есть свой heap, в котором аллоцируются объекты. Когда объект освобождается сборщиком мусора, освобождается место именно в хипе Java, а не в общесистемном. Сборщики мусора могут иметь разные стратегии того, как они отдают память обратно системе. Кроме того, JVM использует и системный heap для своих нужд, например для стеков ваших потоков (которых вы создаете множество).

Ответ 2



Это обычное поведение JVM. JVM управляет памятью через некоторый промежуточный артефакт, т.н. heap - большой (огромный) кусок памяти, в котором по мере необходимости создаются (и удаляются) объекты, но сам heap как был аллоцирован, так и остается. В целом JVM может отдать часть heap обратно ОС, но прямых способов воздействия на этот процесс нет, плюс из-за характера использования с этим могут быть некоторые проблемы (например, сначала придется компактить содержимое хипа).

Может ли gc удалить только что созданный объект до присвоения переменной?

#java #сборщик_мусора


Допустим у нас есть такая строчка:

Object object = new Object()


Сценарий:


Был создан объект new Object(), но ссылка него еще не была присвоена переменной object. 
Был вызван GC. На наш объект нет
ссылки и он его удаляет. 
Переменная object остается без объекта.


Не думаю, что такое возможно, но не понимаю почему.
    


Ответы

Ответ 1



Потому что ссылка есть всегда. Ну сами подумайте, если сразу после окончания работы конструктора ее нет, то что мы потом присваиваем в переменную? Если вы посмотрите на байт-код создания объекта, то увидите что-то вроде 0: new #2 // class java/lang/String // память заказана, ссылка на неициализированный объект положена в стек операндов 3: dup // ссылка раздублирована 4: invokespecial #3 // Method java/lang/String."":()V // один экземпляр ссылки передан параметром в конструктор и там пропал 7: astore_0 // второй экземпляр записали в локальную переменную вот эти ссылки, лежащие в стеке операндов, так же учитываются сборщиком мусора, как и ссылки из переменных.

Ответ 2



Насколько я понял вопрос, речь идет о том, что: может ли мусорщик убрать объект между моментом его создания и присвоения к переменной Ответ, нет не может. Идеология работы сборщика мусора в Java примерно такая (псевдокод): void allocMemory(int n) { //просьба аллокации n байт из кучи if (heapTop - heapStart > n) //проверяем есть ли место в куче сollectGarbage(); //вызываем сборщик мусора heapStart += n; //обновляем указатель на начало свободной кучи } Соответственно сборка мусора будет происходить только перед созданием объекта, но не после него. Понятно, что в реале все по другому, учитывая фрагментацию кучи и многопоточность, но в целом псевдокод приблизительно такой. P.S. Вызов System.gc() - это не вызов сборщика мусора, а всего лишь просьба прибрать мусор

Ответ 3



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

Java.Польза от ссылок(SoftReference, WeakReference , PhantomReference)

#java #jvm #сборщик_мусора


Используя различные ссылки можно получить большую скорость работы сборщика мусора
или для иных целей используются ссылки?    


Ответы

Ответ 1



Разница не в скорости, разница в том, как сборщик мусора будет работать с объектом по ссылке. SoftReference — это самая сильная из всех перечисленных ссылок. Если на объект не осталось больше нормальных ссылок, а только SoftReference, объект не будет съеден сборщиком мусора до тех пор, пока реально не возникнет ситуация нехватки памяти. Хороший пример использования для таких ссылок -- кеш больших картинок в памяти. Если память исчерпалась, картинку выбросит сборщик мусора, и вы сможете перечитать её с диска, когда она снова вам понадобится. WeakReference слабее: она не увеличивает дополнительно время жизни объекта, на который ссылается, и если на объект есть не более чем слабые ссылки, сборщик мусора может убрать его когда ему вздумается. Хороший пример использования для таких ссылок -- добавить дополнительную информацию об каком-то объекте. Для этого вы держите в своём контейнере не сам объект, а лишь WeakReference на него, вместе с необходимой информацией, тем самым вы не мешаете объекту умереть вовремя и не меняете свойства остальной части программы. Имея на руках SoftReference или WeakReference на ещё живой объект, вы можете получить настоящую ("сильную") ссылку, и предотвратить съедение этого объекта сборщиком мусора. Имея сильную ссылку, вы можете работать с объектом как обычно. PhantomReference ещё слабее. Она не только не предохраняет объект от уборки, она даже не даёт возможности получить сильную ссылку. Вы можете только узнать, что объект собирается умереть, и предпринять какие-то действия по очистке; предотвратить смерть объекта вы не сможете. При создании SoftReference, WeakReference вы можете, а при создании PhantomReference должны указать ReferenceQueue (хотя тут можно указать null, это обычно бессмысленно). После того, как объект будет убран сборщиком мусора, ссылка попадает в указанную вами ReferenceQueue. Для нефантомных ссылок при добавлении в очередь финализатор уже выполнен и память объекта уже освобождена, но для фантомных добавление происходит после вызова финализатора до очистки памяти. Вы можете по сути не объявлять дорогой финализатор, а воспользоваться фантомной ссылкой из очереди для того, чтобы самостоятельно освободить ассоциированные ресурсы. (Для этого вы не сможете использовать сам объект, т. к. сильную ссылку на него невозможно получить из фантомной ссылки; но вы можете унаследоваться от PhantomReference, чтобы добавить нужную информацию в объект-ссылку.) Ещё одно отличие, как подсказывает @gstackoverflow в комментариях, состоит в том, что фантомную ссылку вы должны очистить вручную, иначе объект будет оставаться (фантомно) достижим. Только после этого объект будет окончательно удалён. Источники: https://stackoverflow.com/q/3329691/276994 https://habrahabr.ru/post/169883/ http://www.javaportal.ru/java/articles/referenceclasses.html

Ответ 2



Скоростью управлять не получиться. Но более эффективно использовать память - да. PhantomReference Вам навряд ли придется использовать. Почитайте о них.

пятница, 24 января 2020 г.

Можно ли писать на С++ со сборщиком мусора?

#cpp #сборщик_мусора


Пишу на С/С++, но вижу, что языки со сборкой мусора набирают популярность. Опять
же надоело искать утечки памяти. В связи с этим вопрос:


Есть ли сейчас технологии, чтобы писать на С++, но пользоваться сборщиком мусора?
Типа завел в программе объект - сборщик мусора и все операторы new перенаправляются
на него, а он следит за освобождением памяти?

    


Ответы

Ответ 1



Есть давняя древняя классика: http://www.hboehm.info/gc/ Дисклеймер: сам не пользовался ни разу.

среда, 22 января 2020 г.

События и сборка мусора в c#

#c_sharp #winforms #события #сборщик_мусора


Начал изучать концепцию событий в c#. У меня есть следующие классы:

class CustomTimer
    {
        public delegate void DateAndTimeHandler(DateTime dateTime);

        DateAndTimeHandler dateOrTimeUpdated;

        public event DateAndTimeHandler DateOrTimeUpdated
        {
            add { lock (this) dateOrTimeUpdated += value; }
            remove { lock (this) dateOrTimeUpdated -= value; }
        }

        public CustomTimer()
        {
            InitialStartedTime = DateTime.Now;
            int num = 0;

            TimerCallback tm = new TimerCallback(ProcessTime);

            System.Threading.Timer timer = new System.Threading.Timer(tm, num, 0, 1000);

        }

        private void ProcessTime(object obj)
        {
            dateOrTimeUpdated?.Invoke(DateTime.Now);
        }

    }

    class SuperCore
    {
        public CustomTimer timer = new CustomTimer();
    }


И небольшой пример работы с этими классами в WinForms:

public partial class Form1 : Form
{
    public string StringTime
    {
        get { try { return label1.Text; } catch { return ""; }; }
        set { try { Invoke(new Action(() => { label1.Text = value; })); } catch { } }
    }

    SuperCore superCore = new SuperCore();
    public Form1()
    {
        InitializeComponent();
        superCore.timer.DateOrTimeUpdated += TimeUpdate;
    }

    void TimeUpdate(DateTime dt)
    {
        StringTime = dt.ToString();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        GC.Collect();
    }
}


Я заметил, что при сборке мусора у меня перестает обновляться время на label1, т.е.
происходит уничтожение события. Чтобы проверить это и убедиться, что так и есть, я
при нажатии на button1 вызываю сборщик мусора. Гипотеза подтвердилась.

Это приложение является просто демонстрацией проблемы.

Вопрос:
Как мне сделать так, чтобы подписка на событие сохранялась? Чтобы время обновлялось
на label1?
    


Ответы

Ответ 1



Необходимо сделать timer полем класса CustomTimer. Код класса CustomTimer будет выглядеть так: class CustomTimer { public delegate void DateAndTimeHandler(DateTime dateTime); DateAndTimeHandler dateOrTimeUpdated; public event DateAndTimeHandler DateOrTimeUpdated { add { lock (this) dateOrTimeUpdated += value; } remove { lock (this) dateOrTimeUpdated -= value; } } System.Threading.Timer timer; public CustomTimer() { int num = 0; TimerCallback tm = new TimerCallback(ProcessTime); timer = new System.Threading.Timer(tm, num, 0, 1000); } private void ProcessTime(object obj) { dateOrTimeUpdated?.Invoke(DateTime.Now); } } За ответ в комментариях на вопрос спасибо Alexander Petrov и tym32167.

пятница, 10 января 2020 г.

Когда уничтожается ValueType и семантика работы GC с ValueType

#net #память #сборщик_мусора #clr #stack


Ходят мифы и легенды,мол ValueType удаляется посредством GC(то бишь GC деаллоцирует
как ReferenceType,так и ValueType).
Но на самом то деле это не так. К примеру у нас есть код:  

class A {}
class B
{
    void TestMethod()
    {
        A a = new A();
        int x = 100;
    }
}  


в контексте(Scope) метода TestMethod(), создается объект(ReferenceType) типа А,а
так же переменная X(ValueType).   

По завершению работы метода, переменная X уничтожается,а объект типа А теряет ссылку
на объект,и становится претендентом для удаления от GC.  

Иными словами,ValueTypе существует в контексте до тех пор,пока выполняется,и соответственно
Stack, по типу метода Pop() сам удалит эти данные из памяти, и никакого участия в этом
не принимает GC, поэтому ValueType и работает быстрее (хотя все зависит от задачи).   

И сам вопрос,всегда ли это так работает? (читал разные статьи,иногда пишут,что это
происходит только тогда,когда стек забивается, т.е. доходит до заполнения)   

Что делает CLR,когда стек уже почти переполнен,а все данные в нем к примеру являются
ссылками на объекты в куче?
Как и когда удаляются пользовательские структуры? Как именно CLR решает удалять ли
данные из стека или оставить их еще существовать N-ое кол-во времени!??
    


Ответы

Ответ 1



Локальные переменные типов ValueType [на которых нет замыканий из анонимных методов и лябмд] лежат прямо в стеке или в регистрах процессора (как захочется оптимизатору). Вы можете прямо посмотреть, как выполняется ваш код, нажав правой кнопкой по нему в отладке, и выбрав Go To Disassembly, может быть это прояснит картину. Вот как это выглядит в отладочном режиме (что выключает оптимизации). Я добавил комментарии в важных местах: { 025B2E48 push ebp // это так называемый пролог функции 025B2E49 mov ebp,esp // https://en.wikipedia.org/wiki/Function_prologue 025B2E4B push edi // суть его - сохранить текущее положение 025B2E4C push esi // стека в "базовый указатель" - [e]bp 025B2E4D push ebx в стек запихнули значения 3-х регистров, так что его указатель теперь отличается уже на 12 от того, который был в начале функции 025B2E4E sub esp,3Ch esp - это указатель на начало стека. Уменьшить его на 3Ch - это выделить в стеке 3Сh (60) байт под локальные переменные (или другие накладные расходы) к этому моменту он уже отличался на 12 от значения, которое лежит в ebp, так что локальные переменные находятся в диапазоне по адресам от [ebp-13] до [ebp-72]. Он же [ebp-0Dh] до [ebp-48h]. Потом делаем кучу проверок и долго и мучительно создаем объект (это все из-за отладочного режима). Я пропущу большую часть кода, она не имеет отношения к вопросу: 025B2E51 mov esi,ecx 025B2E7C nop A a = new A(); 025B2E7D mov ecx,700F98h 025B2E82 call 024130F4 025B2E87 mov dword ptr [ebp-48h],eax 025B2E8A mov ecx,dword ptr [ebp-48h] 025B2E8D call 025B0D18 025B2E92 mov eax,dword ptr [ebp-48h] и вот наконец ложим указатель на созданный объект в стек (в ebp лежит положение стека на момент начала вызова функции) 025B2E95 mov dword ptr [ebp-40h],eax С целым числом попроще - просто запихиваем нужное значение в относительно epb - т.е. относительно начала стека на момент функции. int x = 100; 025B2E98 mov dword ptr [ebp-44h],64h } 025B2E9F nop А вот теперь фокус. Берем и загружаем в указатель стека значение, которое в нем было сразу после 025B2E4D push ebx. По сути это esp = ebp-0Ch 025B2EA0 lea esp,[ebp-0Ch] 025B2EA3 pop ebx 025B2EA4 pop esi 025B2EA5 pop edi и после следующей строчки получаем значение esp равное тому, которое было в начале функции. 025B2EA6 pop ebp 025B2EA7 ret За счет чего при этом выделалась и освобождалась память в стеке? Выделалась за счет уменьшения указателя стека на нужное значение. Освобождалась - за счет восстановления старого значения указателя. Расходов на разрушение или "сброрку мусора" локальных переменных при этом не было. Это стандартный механизм на x86, так что можно считать что так происходит почти всегда. По возврату из функции значение Stack Pointer восстанавливается в то, что было до ее вызова.

вторник, 7 января 2020 г.

Когда garbage collector ненужен и вреден?

#производительность #сборщик_мусора


Перечислите, пожалуйста, задачи (желательно поконкретнее), при которых влияние garbage
collector на производительность программы критична и несовместима с оптимальной работой
программы?    


Ответы

Ответ 1



любая задача которой паузы критичны. К примеру, проигрывание видео (пользователю не сильно приятно, когда видео просто останавливается, потому что gc решил поработать) кардиостимулятор (подожди пользователь, пару ударов пропустим, у нас тут gc.:) ) другие приборы жизнеобеспечения. автопилот сетевые real-time игры. Только прицелился, а тут "сделай паузу, скушай батончик".

понедельник, 6 января 2020 г.

Garbage collector: перемещение объекта из поколения в поколение

#c_sharp #сборщик_мусора


Когда у нас заполняется нулевое поколение кучи, происходит анализ этого поколения:
удаляются "мёртвые"  объекты и перемещаются "выжившие" в следующее поколение - 1. 
Вопрос: если в поколении 1 недостаточно места для приёма объектов из нулевой кучи,
то что происходит? Очистка первого поколения? 




UPDATE

Цитата (C# 5.0 in Nutshell, Albahari):


  Среда CLR сохраняет раздел Gen() относительно небольшим (максимум 16 Мбайт в 32-битной
версии для рабочей станции, с типичным размером от нескольких сотен Кбайт до нескольких
Мбайт). Когда раздел Gen() заполняется, сборщик мусора GC инициирует сборку Gen() —
что происходит относительно часто. Сборщик мусора применяет похожий порог памяти к
разделу Gen1 (который действует как буфер для Gen2), поэтому сборки Gen1 являются тоже
относительно быстрыми и частыми. Однако полные сборки мусора, включающие Gen2, занимают
намного больше времени и, таким образом, происходят нечасто. Результат полной сборки
мусора показан на рис. 12.2.

    


Ответы

Ответ 1



Достижение поколением порогового размера — всего лишь триггер для начала сборки мусора. Когда общий размер объектов в Gen0 станет больше порога, запустится сборщик мусора. Когда сборщик мусора запустится, он смотрит, не превышает ли суммарный размер объектов в Gen2 порог. Если да, запускается полная, медленная сборка мусора всех трёх поколений. Если Gen2 в порядке, но суммарный размер объектов а Gen1 превышает порог для Gen1, запускается ускоренная сборка, которая рассматривает только Gen0 и Gen1. Если же Gen1 тоже в порядке, то запускается ускоренная сборка только Gen0. Если после сборки Gen0 переполнится Gen1 (то есть, суммарный размер Gen1 станет выше порога), ничего не произойдёт до следующего запуска сборщика мусора. А когда он таки запустится (по переполнению Gen0), он обнаружит переполнение поколения Gen1 и соберёт его тоже. (Кстати, размеры порогов на текущий момент не фиксированы, и фреймворк динамически подгоняет их во время пробега программы.) Литература: Jeffrey Richter. The Managed Heap and Garbage Collection in the CLR.

Ответ 2



Если произойдет "переполнение" (таки у поколений есть пороги) для первого поколения, GC вызовет проверку на первом поколении. Выжившие объекты перейдут во второе, не выжившие - умрут, никакой магии. Рихтер в CLR via C# в главе 21 "Автоматическое управление памятью (уборка мусора)" хорошо описал это.

пятница, 3 января 2020 г.

Отслеживание действия garbage collector в java

#java #сборщик_мусора


Есть ли какой нибудь способ отслеживать в программе действия garbage collector? например
писать в логи все его похождения. вот он запустился, прошелся по классу, что затронул?
young/old и пр. типы памяти. 
    


Ответы

Ответ 1



Вы можете включить логирование сборок мусора передав JVM параметр -Xlog:gc* при запуске программы.

вторник, 31 декабря 2019 г.

Поведение сборщика мусора по отношению к структурам

#c_sharp #структуры #сборщик_мусора #производительность


Недавно прочитал статью Предельная производительность: C#

Из-за того, что структуры хранятся в stack’е, они не требуют сборки мусора

поясните пожалуйста, это действительно так?    


Ответы

Ответ 1



Краткое содержание. Нет, структуры не обязательно хранятся в стеке, а объекты -- в куче. Да, с хорошими шансами структура всё же попадёт в стек. Нет, вам не стоит на это рассчитывать, пользоваться этим и пытаться оптимизировать таким образом. На самом деле следует понимать простую вещь: выделение переменной на стеке дешевле, поскольку её можно легко уничтожить, не включая в цикл сборки мусора. Выгода на самом деле только в этом. (Аллокация что на стеке, что в куче -- не более, чем увеличение одного указателя, она очень быстрая.) Очевидно, оптимизатор будет размещать в стеке те переменные, которые, как он может доказать, не нужны после смерти текущего фрейма. Часто про структуры можно такое доказать, но не всегда. Например, структура может быть частью объекта класса, и должна умереть вместе с классом. Или метод будет неявно переписан в стиле продолжений, например, если это генератор (yield return & Co.) или Task<> с async/await. Или переменная попала в замыкание некоторой лямбда-функции. И так далее. Но обычно структуры не нужны после отработки метода, так что оптимизатор может вытеснить их в стек. С другой стороны, про некоторые объекты можно тоже утверждать, что они не нужны после окончания фрейма -- и тогда оптимизатор тоже имеет полное право (но не обязанность, конечно) разместить и их на стеке. Обратите внимание на такую тонкость: если вы возвращаете из метода структуру, вы на самом деле возвращаете её копию, поэтому структура, с которой вы работали, может попасть в стек. С классами же не так: они копируются не по значению, а по ссылке, поэтому возвращаемый объект переживает создавшую его функцию, и следовательно не имеет права жить в стеке. Использованы материалы из блога Эрика Липперта, на которые была ссылка выше. Добавлю ещё пару цитат из Эрика: Использование стека для локальных переменных-структур -- всего лишь оптимизация, которую CLR выполняет для вас. Существенная особенность структур -- семантика копирования по значению, а вовсе не то, что в некоторых случаях их уничтожение может быть оптимизировано рантайм-библиотекой. В подавляющем большинстве программ, выделение и уничтожение локальных переменных не будут критически важным фактором производительности. Превращение типа, который должен на самом деле быть ссылочным типом, в структуру -- это нано-оптимизация, дающая выгоду в пару наносекунд, и вероятно не стоящая того. На вашем месте я бы проводил такую оптимизацию только если данные профилирования покажут, что существует реальная, большая проблема у ваших реальных клиентов, которую можно исправить использованием структур. Не имея таких данных на руках, я всегда бы делал выбор между классами и структурами основываясь на том, представляет ли тип семантически значение или ссылку на что-то. (То есть, имеет ли объект смысл помимо значения, содержащегося в нём, обладает ли он самостоятельной сущностью -- VladD)

Ответ 2



Из-за того, что структуры хранятся в stack’е, они не требуют сборки мусора поясните пожалуйста, это действительно так? Если речь идет о локальных переменных, то это действительно так. GC работает с кучей. Очевидно, что структуры не всегда хранятся в стеке. Например, если какой-то класс содержит поля, являющиеся структурами, то память под них однозначно будет выделена в куче, и, следовательно, будут уничтожаться GC. P.S.: Если структура содержит управляемые поля, то память под эти поля выделится в куче, а в стеке окажутся лишь ссылки на эти управляемые поля. Довольно логично, но некоторые упускают это из виду.

понедельник, 30 декабря 2019 г.

Правильная реализация метода Dispose

#c_sharp #классы #сборщик_мусора


Есть класс А, у него 4 текстовых поля А1,А2,А3,А4.
Я хочу реализовать метод  Dispose для класса, т.к. в его полях находится большой
текст, а используется он всего 1 раз.

Скажите, будет ли достаточно в моем случае, если сделаю так?

Dispose()
{
   А1 = null;
   А2 = null;
   А3 = null;
   А4 = null;
}


Или может нужно просто сделать А1 = ""?
    


Ответы

Ответ 1



Если вы не работаете в своем классе с неуправляемыми ресурсами и вы не планируете использовать свой класс вместе с using, то реализовывать IDisposable не нужно. И обнулять ссылки тоже не нужно. Метод Dispose определен в интерфейсе IDisposable, который используется в .NET Framework в разных классах. На реализацию IDisposable можно посмотреть в исходниках. (Надесь никто не будет спорить, что в исходниках .NET правильные реализации этого интерфейса). Там же можно посмотреть на комментарии разработчиков .NET На скриншоте страницы справа - комментарии разработчиков, а слева - это ссылки на реализацию IDisposable в разных классах. Один из примеров реализации IDisposable из .NET Framework: class SimpleMonitor : IDisposable { public void Enter() { ++ _busyCount; } public void Dispose() { -- _busyCount; } public bool Busy { get { return _busyCount > 0; } } int _busyCount; } Взят тут. Ничего особенного, это если не надо работать с неуправляемыми ресурсами. ВАЖНО: при работе с неуправляемыми ресурсам надо использовать SafeHandle. Пример тут, а краткое описание под скриншотом. Почитайте описание класса в MSDN, посмотрите на реализацию в исходниках, также посмотрите базовые классы. Очевидно, что там все достаточно сложно. Поэтому не придумывайте велосипеды, несмотря на то, что некоторые это предлагают с серьезным видом :) Как использования SafeHandle? Например надо с помощью WinAPI функции FindFirstFileEx получить информацию о папке. Функция возвращает HANDLE -- это неуправляемый ресурс, который обязательно надо освободить с помощью WinAPI функции FindClose. В такой ситуации надо определить класс SafeHandle using Microsoft.Win32.SafeHandles; class SafeHandle : SafeHandleZeroOrMinusOneIsInvalid { private SafeHandle() : base(true) { } protected override bool ReleaseHandle() { return FindClose(this.handle); } } И указать SafeHandle в определении функции static extern SafeHandle FindFirstFileEx(...) В своем классе пишем var h = FindFirstFileEx(...); Таким образом мы получаем ссылку на специальную обертку над неуправляемым ресурсом. И даже если произойдет неожиданное прерывание потока или переполнение стека и т.д., то будет вызвана функция FindClose, т.е. гарантированно будет закрыт дескриптор неуправляемого ресурса. Работающий пример тут.

Ответ 2



Отвечая на ваш вопрос об обнулении ссылок -- нет, обнулять их не нужно, поскольку это не поможет. Более того, в вашем случае метод Dispose вам совсем не нужен. Почему, читайте ниже. Существует только две ситуации, когда необходимо реализовывать IDisposable: В классе есть управляемые (IDisposable) ресурсы В классе есть неуправляемые ресурсы В классе есть управляемые (IDisposable) ресурсы У всех таких ресурсов нужно вызвать метод Dispose() или его аналог (например, Close()). Зануллять такие ресурсы в 99.9% случаев нет необходимости, поскольку они в любом случае будут собраны сборщиком мусора. (Зануллять можно, если эти ресурсы занимают большой объем памяти и вы хотите "посигнализировать" сборщику мусора чтобы он побыстрее их собрал. Но никаких гарантий это не дает.) public sealed class SingleApplicationInstance : IDisposable { private Mutex namedMutex; private bool namedMutexCreatedNew; public SingleApplicationInstance(string applicationName) { this.namedMutex = new Mutex(false, applicationName, out namedMutexCreatedNew); } public bool AlreadyExisted { get { return !this.namedMutexCreatedNew; } } public void Dispose() { namedMutex.Close(); } } В классе есть неуправляемые ресурсы Реализация Dispose() для таких классов должна "закрыть" ресурс (как -- зависит от самого ресурса) и вызвать GC.SuppressFinalize(this);. Плюс обязательно должен быть реализован финализатор, в котором должно вызываться "закрытие" ресурса. Т.о. гарантируется, что ресурс будет закрыт в любом случае -- либо программистом (при этом финализатор вызва не будет), либо сборщиком мусора (путем вызова финализатора). public sealed class WindowStationHandle : IDisposable { public WindowStationHandle(IntPtr handle) { this.Handle = handle; } public WindowStationHandle() : this(IntPtr.Zero) { } public bool IsInvalid { get { return (this.Handle == IntPtr.Zero); } } public IntPtr Handle { get; set; } private void CloseHandle() { if (this.IsInvalid) { return; } if (!NativeMethods.CloseWindowStation(this.Handle)) { Trace.WriteLine("CloseWindowStation: " + new Win32Exception().Message); } this.Handle = IntPtr.Zero; } public void Dispose() { this.CloseHandle(); GC.SuppressFinalize(this); } ~WindowStationHandle() { this.CloseHandle(); } } internal static partial class NativeMethods { [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool CloseWindowStation(IntPtr hWinSta); } Полная версия о правильном применении IDisposable -- в моем переводе на Хабре.

Ответ 3



Паттерн IDisposable в вашем случае не применим, т.к. он служит для освобождения внешних ресурсов, а не памяти. Ресурс в это случае - это нечто, что требует явного закрытия - файл, сокет, транзакция. Память же в .NET ручного освобождения не требует. Кроме освобождения ресурсов IDisposable часто реализуют только ради использования синтаксиса using, но это тоже явно не ваш случай. Освобождением памяти в .NET занимается сборщик мусора. Когда он решает, что память стоило бы немного освободить, он отслеживает достижимость объектов от т.н. корней - локальных переменных, глобальных статический полей и прочих способов хоть как-то добраться до объекта из кода. Предположим, у вас есть локальная переменная, ссылающаяся на объект вашего класса. Сборщик мусора проверяет достижимость: локальная переменная → ваш объект → большая строка видит, что строка используется, и не освобождает из нее память. Но вот вы вышли из метода. Или код в методе прошел дальше последнего упоминания вашей переменной в коде. На ваш объект больше нет ссылок ниоткуда: ваш объект → большая строка И объект, и большая строка недостижимы. И сборщик мусора их спокойно сжигает при следующей сборке. Изменило бы ситуацию зануление ссылок (в Dispose, или в другом методе)? Совсем нет. Зануление в этой ситуации - совершенно излишне.

Ответ 4



Да, можно. null. Но вообще, есть вероятность, что это просто лишнее. Такое имеет смысл делать только если эти строки реально влияют на память, а объект, содержащий их, живёт значительно дольше. В любом случае, странно использовать именно dispose для этого. Кстати, рекомендуется проверять, был ли вызван dispose в других методах. В твоём случае, вероятно, в методе, использующем эти строки.

воскресенье, 29 декабря 2019 г.

Когда ручной вызов GC.Collect() оправдан?

#c_sharp #net #сборщик_мусора


Часто нахожу в коде вызовы GC.Collect(), например при работе с графиков через GDI+.

В умных книжках пишут, что его никогда не нужно вызывать самому.

Собственно вопрос, а есть ли оправданные случаи, когда его нужно вызывать или это
признак плохого когда?
    


Ответы

Ответ 1



Обычно не нужно. Иногда имеет смысл это делать: После уничтожения большого количества объектов (например, закрытия формы с большим количеством элементов) Когда приложение имеет четко выраженные периоды активности и простоя. Если принудительно вызвать сборку мусора в период простоя, уменьшится вероятность того, что она произойдет в период активности и затормозит выполнение кода. Ссылки: When to call GC.Collect() When is it acceptable to call GC.Collect?

Ответ 2



Просто так, без веских причин, и без точного понимания, что происходит при сборке мусора, дергать GC.Collect не стоит. Рантайм сам вполне справляется с автоматической сборкой мусора. Не стоит дергать Collect ни по таймеру, ни "на всякий случай" - это не даст никакого реального "ускорения" или улучшения. Более-менее реальные причины для вызова Collect: Вы точно знаете что в вашем приложении только что стало мусором огромное количество объектов И при этом мгновенно сократить потребление памяти приложением достаточно критично. Если мгновенность не требуется (а она не требуется почти никогда) - хватит автосборки. В рамках теста, который меряет потребление памяти. В рамках теста, который проверяет работу кода с WeakReference. При входе / выходе из режима GCLatencyMode.LowLatency и GCLatencyMode.SustainedLowLatency - стоит вызвать GC.Collect(2, GCCollectionMode.Forced) (возможно) при выходе из региона GC.TryStartNoGCRegion / GC.EndNoGCRegion. Топик на enSO: When is it acceptable to call GC.Collect?

Создание объекта без присвоения ссылки

#java #сборщик_мусора


Есть класс, в конструкторе которого создается асинхронный слушающий сокет. Вся работа
с сокетом реализована в этом классе и внешняя ссылка на этот класс не нужна.

Вопрос, на сколько корректен такой способ создания объекта класса

new Server();


т.е. без присвоения созданного экземпляра переменной. Не прибьет ли такой объект
сборщик мусора?
    


Ответы

Ответ 1



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

пятница, 27 декабря 2019 г.

Корректное удаление узлов из DOM, у которых зарегистрированы обработчики

#javascript #dom #события #сборщик_мусора


Возникает необходимость периодически, по средством AJAX, перезаписывать содержимое
блока, по средством присвоения нового innerHTML, при этом на дочерние элементы этого
блока были назначены обработчики, по средством element.addEventListener, которые я
явно не удаляю, и при этом, после добавления элементов, регистрирую ещё и новые.
В большинстве случаев, содержимое старого блока, в точности совпадает с новым, но
при этом я всё равно его перезаписываю и повторно назначаю обработчики.

Меня интересует, при перезаписи дочерних элементов блока, по средством innerHTML
удаляются ли также, вместе со всеми дочерними элементами, их объявленные обработчики,
или же они остаются вместе с новыми, которые я каждый раз регистрирую по новой?

И если да, то происходит ли это также с element.remove() и parent.removeChild(element)?
    


Ответы

Ответ 1



Ваш случай никоим образом не относится напрямую к dom элементам или к слушателям. Это обычный случай на понимание сборщика мусора или как его называют Garbage Collection или просто GC. Его работа очень проста - если до объекта можно добраться по ссылкам начиная с корневого элемента window, то он не может подвергнуться утилизации. Другими словами, если передать в объект a ссылку на объект b, а затем удалить ссылку на объект a, то объект b его не удержит. let a = { b: null }; let b = { a: a }; a.b = b; a = null; // объект { b: a } будет удален из памяти. Что же происходит, когда мы подписываемся под события. Сначала нужно вспомнить что такое EventDispatcher - export default class EventDispatcher { constructor(){ this.handlers = {}; } addEventListener(type, handler){ let handlerAll = this.handlers[ type ]; if( ! handlerAll){ handlerAll = this.handlers[ type ] = []; } handlerAll.push( handler ); } removeEventListener(type, handler){ let handlerAll = this.handlers[type]; if(handlerAll){ let index = handlerAll.indexOf( handler ); if(index > -1){ handlerAll.splice( index, 1 ); if( ! handlerAll.length) { delete this.handlers[type]; } } } } dispatchEvent(type, event){} } Это очень упрощенный пример, который не содержит комментариев, только потому, что там все до банальности легко. Но особое внимание стоит обратить на то, что передаваемый слушатель просто сохраняется в EventDispatcher. Другими словами - let EventDispatcher = { handlerAll: [ ] }; let handler = function(){}; EventDispatcher.handlerAll.push( handler ); EventDispatcher = null; //все, EventDispatcher канул вне небытия // и handler не смог его удержать. Получается что если ситуация очень простая, почти нереальная, то после удаления объекта из dom он благополучно удалится. Но дело в другом! Остается очень большая вероятность что в памяти останется висеть тот контекст, в котором происходила подписка и это ещё при лучшем стечении обстоятельств. Ведь самая главная причина по которой было одобрено правило хорошего тона "всегда удалять слушатели" является замыкание, которое в javascript является основой. Поэтому я не буду описывать все возможные причины утечек, а лишь ещё раз скажу, что хорошим тоном считается всегда удалять слушатели, да и вообще чистить объекты вручную. Ну а если быть совсем откровенными друг с другом, то нужно понимать что такое Element возвращаемый нам при запросе. когда мы просим выдать нам нужный элемент по какому-то идентификатору, например document.querySelector(#some-id);, программа возвратит нам объект, который представляет из себя совокупность свойств и методов для управления объектом созданным на более низком уровне. И когда Вы пишите так как ниже, то сохраняете ссылку на элемент в переменную - let element = document.querySelector( `#some-id` ); element.addEventListenr( 'click', element_clickHendler ); function element_clickHendler(){} Что в свою очередь при удалении из родителя с помощью любого известного способа, удалит объект лишь из дисплей листа, проще говоря с экрана. Но ссылка так и будет держать объект элемента и слушатель будет в рабочем состоянии, до тех пор пока будет существовать хоть одна ссылка на него. document.querySelector( `#some-id` ).addEventListenr( 'click', element_clickHendler ); function element_clickHendler(){} Если Вы сделаете так как выше, то при удалении элемента любыми известными способами удалит и элемент из дисплей листа и объект элемента. Следовательно и слушатель удалится вместе с объектом элемента. И все это из-за того что ссылка на возвращаемый при поиске объект не сохраняется. Что же касается способа удаления, то больше чем уверен что нет никакой разницы на уровне программы. Ведь объект всегда можно удалить только одним способом, а все остальное разнообразие является синтаксическим сахаром для удобной эксплуатации.

понедельник, 23 декабря 2019 г.

Mark-and-sweep алгоритм сборки мусора

#java #jvm #сборщик_мусора


Насколько я понимаю, есть два фундаментальных подхода к сборе мусора:


Copying collectors
Mark-and-sweep


Оба алгоритма описываются в этой статье. По второму алгоритму автор статьи пишет:


  
  Объекты аллоцируются в памяти
  Нужно запустить GC
  Приложение приостанавливается
  Сборщик проходится по дереву объектов, помечая живые объекты
  Сборщик проходится по всей памяти, находя все не отмеченные куски памяти, сохраняя
их в "free list"
  Когда новые объекты начинают аллоцироватся они аллоцируются в память доступную
в "free list"
  


Если я правильно понимаю, то объекты, которые необходимо удалить, переносятся в "free
list". Но где, собственно, происходит удаление этих объектов? Или они явно не удаляются,
а просто перезаписываются при создании новых объектов?
    


Ответы

Ответ 1



Насколько я понимаю, нет необходимости производить дополнительные операции по удалению объектов в памяти. JVM будет создавать новые объекты на месте старых. Статья о том, как производится создание и удаление объектов в C++ The delete operator does not actually delete anything. It simply returns the memory being pointed to back to the operating system. The operating system is then free to reassign that memory to another application (or to this application again later). Думаю в java аналогично, только там не операционная система, а jvm. (Не совсем уверен, буду рад, если кто поправит)

Ответ 2



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

воскресенье, 22 декабря 2019 г.

Сборщик мусора и метод finalize в Java

#java #сборщик_мусора


Из одной статьи


  Сборщик очистит финализированный объект за два шага: в первый выполниться finalize,
а во второй соберется.


И так вопросы


Когда сборщик встречает финализированный объект он сначало отправляет его на очередь.
И код в методе finalize выполняется уже в очереди. Когда метод выполнится он станет
доступен сборщику и уничтожится при следующей сборке. Я правильно понял?
Stop the world действует на поток Finalizer?

    


Ответы

Ответ 1



Да, вы правы - после выполнения метода finalize() объект должен быть повторно собран сборщиком мусора (и это считается серьезной проблемой метода finalize() - он мешает сборщику мусора освобождать память). К слову, не обязательно объект будет доступен для сборки сразу же - метод finalize() может сохранить куда-нибудь ссылку на объект. Подобная ситуация называется "возрождением" объекта и, вообще говоря, считается антипаттерном. Главная проблема такого трюка - в том, что "возродить" объект можно только 1 раз. Stop the world безусловно действует на поток Finalizer, поскольку это такой же поток как и все остальные.

четверг, 19 декабря 2019 г.

OutOfMemoryError: GC overhead limit exceeded при параметрах -Xmx8192M -XX:-UseGCOverheadLimit

#java #junit #jvm #сборщик_мусора


jUnit тесты работают с большими объемами данных, в частности, данная ошибка возникает,
когда считываем данные из базы в объект.

ОЗУ - 16 Гб, 4 виртуальных процессора

когда возникла ошибка

java.lang.OutOfMemoryError: GC overhead limit exceeded
at sun.reflect.GeneratedConstructorAccessor47.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
at java.lang.Class.newInstance(Class.java:442)


то я добавил параметр -XX:-UseGCOverheadLimit
но получил следующую ошибку:

java.lang.OutOfMemoryError: GC overhead limit exceeded
    at java.lang.reflect.Field.copy(Field.java:150)
    at java.lang.reflect.ReflectAccess.copyField(ReflectAccess.java:144)
    at sun.reflect.ReflectionFactory.copyField(ReflectionFactory.java:309)
    at java.lang.Class.copyFields(Class.java:3115)
    at java.lang.Class.getFields(Class.java:1557)
    at main.java.DataFormat.DbTable.loadObjectFromResultSet(DbTable.java:101)
    at main.java.DataFormat.DbTable.getEntities(DbTable.java:57)


все параметры JVM -Xmx8192M -XX:-UseGCOverheadLimit

в момент появления ошибки были следующие параметры системы: 
ОЗУ стабильно на 46% (7400Мб), ЦПУ 77%-82% (82% в момент появления ошибки) 

подскажите, как исправить данную проблему? 

Спарвка


  
  java.lang.OutOfMemoryError: GC overhead limit exceeded
  
  
  Данная ошибка может возникнуть как при переполнении первой, так и
  второй областей. Связана она с тем, что памяти осталось мало и GC
  постоянно работает, пытаясь высвободить немного места. Данную ошибку
  можно отключить с помощью параметра -XX:-UseGCOverheadLimit, но,
  конечно же, её надо не отключать, а либо решать проблему утечки
  памяти, либо выделять больше объема, либо менять настройки GC.


Ответ
String объект слишком много весит. а у меня там очень много стрингов. Для примера,
посчитали файл, который весит 250 МБ, имеет 1600 символов в строке и 220 000 строк.
Заполняю в ArrayList lines, где каждая строка записана как объект из 250 полей,
которые принимают значение из 1600 символов. Такой объект весил чуть больше 6Гб. 

Решение: увеличил память до 14Гб. теперь проблема в том, что 6Гбайтный объект не
помещается в куче, надо думать что-то другое и пожертвовать временем выполнения теста. 
    


Ответы

Ответ 1



В данном случае ошибка java.lang.OutOfMemoryError: GC overhead limit exceeded говорит о том, что Вы пытаетесь обработать такой объем данных который не помещается в память. В приведенной Вами справке упомянуто, что ошибка выбрасывается, когда область памяти, занимаемая только, что созданными объектами переполняется. Плюс к этому в трассировке стека видно, что ваши классы ( at main.java.DataFormat.DbTable.loadObjectFromResultSet(DbTable.java:101) at main.java.DataFormat.DbTable.getEntities(DbTable.java:57) ) в момент получения ошибки как раз пытаются выгрузить данные из базы. Поэтому простой и неутешительный вывод из вышесказанного тестируйте на меньшем объеме данных или выделяйте больший объем под выгружаем данные(-Xmx12000M) если они туда поместятся, то ошибка исчезнет. Настраивать сборку мусора в данном случае бесполезно т.к. GC не сможет удалить загружаемые объекты потому как они еще используются.

Ответ 2



Начту с основ для лучшего понимания ошибки. JVM имеет две области памяти: Heap Memory и Non-Heap Memory. Heap Memory - хранит объекты; Non-Heap Memory - хранит параметры методов, примитивные типы и т.д. В Вашем случае происходит переполнение Heap Memory, т.к. очень много создается объектов, которые не вмещаются в Heap Memory. Решить проблему можно увеличив Heap Memory (ключ -Xmx), что не всегда помогает, т.к. объем данных может быть больше чем доступно ОЗУ, поэтому лучше реализовать обработку данных из БД порционно, т.е. выгружать часть данных, чтобы объекты поместились в памяти, затем убивать их (присваивать null, чтобы GC разгрузить), замет следующею часть и т.д. P.S. Стоит понимать, что Heap не равно ОЗУ. Heap - это только зарезервированная память для JVM, больше данного резерва JVM использовать не может даже, если ОЗУ в избытке.

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

#c_sharp #net #сборщик_мусора


Допустим, имеется метод внутри класса:

public ICollection GetData()
{
    ICollection rezult;
    var tempCollection = context.Get();
    //doing some stuff;
    return rezult;
}


Как ни крути, мне в коде нужна эта временная переменная. Вопрос состоит в том, нужно
ли "занулять" эту переменную (tempCollection = null;), чтобы GC при сборке мусора понял,
что она уже не нужна, или же это и так будет понятно, посколько она объявлена внутри
метода?
    


Ответы

Ответ 1



При завершении метода все его локальные переменные пропадают (если только не были захвачены замыканием). Отдельно занулять их не нужно.

Ответ 2



Объект, на который ссылается tempCollection, превращается в мусор сразу же после последнего упоминания tempCollection в теле метода - т.е. иногда задолго до того, как метод вернет управление. GC собирает объекты после того, как они станут недостижимыми - т.е. после того, как их, прямо или косвенно, нельзя будет увидеть из т.н. корней - статических полей, локальных переменных и еще пары специфических мест. Как только объект становится недостижим - GC может его спокойно убить. Предположим что ваш метод выгдядит вот так: public ICollection GetData() { ICollection rezult; var tempCollection = context.Get(); // doing some stuff // последнее упоминание tempCollection в методе: var something = tempCollection.SomeProp; // doing some more stuff; return rezult; } Локальная переменная tempCollection упоминается только в верхней половине метода. Ниже этой строчки код никак не может получить доступ к ее содержимому. Так что GC вполне справедливо не считает ее корнем в нижней половине метода. Учитывая это, добавление дополнительного упоминания tempCollection в коде (пусть даже и в виде tempCollection = null) не сократит, а наоборот, чуть-чуть продлит время его жизни. Стоит отметить, что при сборке в Debug, в отличии от Release, JIT заботится о вашем удобстве, и продлевает область жизни tempCollection до конца области видимости (т.е. до конца метода). Делает он это исключительно из соображений удобства при отладке - чтобы вы могли просмотреть значение tempCollection даже на последней строчке метода.