[C++/Java/C#] Освобождение ресурсов
Подсказка. Если ты не делаешь dispose для своих объектов, то их жизнь очень сильно продлевается. Так время проведенное в GC может в несколько раз вырасти. Советую по играться с CLRProfiler, там можно все очень наглядно увидеть.
Так для информации, есть такой документ Improving .NET Application Performance and Scalability -- всего под 1100 стр.
когда бы удалить объект!
дуйте на C#/JavaА файлы ты в Яве тоже не закрываешь? Bitmap это ресурс операционной системы, потому что он иногда занимает видеопамять и т.п. Его "удалением" называется соответствующий системный вызов, и это не имеет отношения к менеджменту памяти, который в Яве такой клевый со сборкой мусора там и т.д. Не умничай короче, срацца про языки надо в других тредах. Где кстати тред "MFC - говно"
"MFC - говно"О, давайте! MFC - говно, Win32 - прикольно, ура WTL
можно не закрывать - закроется сам.
Битмапы и окна, кстати, тоже можно не закрывать (не уничтожать) - это сделается само.
ps
Так делать (не закрывать ресурсы) - не правильно, но сильно облегчает жизнь новичкам.
Ага. Только может так получится, что данные не флашнутся.
Да ладно передёргивать, я спутал кусок памяти в куче с объектом Windows. Ну с кем не бывает? Это к тому, что файлы в Java, как ни удивительно, тоже закрывают ручками.
тоже можно не закрывать (не уничтожать) - это сделается самоТы имеешь в виду финализатор? Ну, да ты и сам знаешь что я на это могу сказать
сильно облегчает жизнь новичкамНе писать нигде delete тоже облегчает жизнь новичкам, ну и что теперь И память по выходу из программы освободится...
Ну, да ты и сам знаешь что я на это могу сказатьА что ты можешь сказать? Мне даже интересно стало
Если раньше отрисовка не остановится (в gdi ресурсы очень ограничены)
Сборка мусора обычно стартует тогда, когда израсходован heap. Да, можно насильно, можно наверное по таймеру или еще как-то, но по логике вещей единственной причиной собирать мусор является закончившаяся куча. Финализатор в котором написано закрыть файл вызовется только при сборке мусора. Но открытые файлы - это же ресурс совсем другой природы, следуя той же логике файлы вроде как нужно закрывать, когда не хватает свободных файловых дискрипторов. На такое я бы даже согласился, а вот когда они закрываются от того, что память кончилась... В огороде бузина, а...
Это с какой скоростью надо захватывать ресурсы?
ps
начиная с win2k - gdi handle-ов что-то в районе 10000 на процесс
т.е. на 4-ех гигах памяти любая программа будет отжирать 4-га памяти?
Когда стартуешь jvm ты ей указываешь какого размера heap захватывать. После этого она на нем "топчется". Может есть какая-то эвристика, чтобы она его расширяла, но это уже тема докторской диссертации...
Но по таймеру-то он все равно сборку мусора делает, а больше ничего и не надо.
Да, согласен, но с файлами еще много причин есть - почему их стоит закрывать.
Но вот gdi-шные объекты, на первый взгляд, можно не закрывать.
Конечно, если они не в многократном цикле создаются.
Он скажет, что если сборка мусора не потребовалась (например, приложение уже закрылось то всякие finalize никто вызывать не будет (нафиг надо, вся память и так возвращается системе). Следовательно, если закрытие файла ты делаешь в finalize то файл может и не зактрыться, а значит, не полностью записаться.
Уж не знаю, кто этот ОН (помните анекдот про C2H5(OH)? но надеяться на сборку мусора, когда в куче и так дофига места, не стоит. У Б. Экеля в Thinking in Java (или C#?) есть пример: выделяем память, ждём 10 секунд - и почему-то накакого finalize.
Хмммм... А в жаве/сшарпе можно создавать объекты на стеке или с поведением как на стеке(при выходе из области видимости или эксепшене)?
в C# для этого обычно keyword using используют. в Java-е все намного страшнее, приходится try/finally городить.
в C# для этого нужно использовать using
Значит я еще долго не буду использовать эти языки... С помощью этого механизма можно так красиво делать fine grained locks
// на шарпе
public class AutoLocker : IDisposable
{
public AutoLocker
{
// блокируем что надо
}
public void Dispose(bool disposing)
{
// разблокируем
}
public static void Main
{
using(AutoLocker myLock = new AutoLocker
{
// делаем что надо, можем кинуть исключение
}
// а тут myLock уже грохнут
}
}
Файнализация выполняется в отдельном потоке для объектов, которые были помечены как мусор. При этом пока для них не вополнилась файнализация, память что под них не освобождается. Поэтому GC их двигать.
В Dispose нужно кроме очищения ресурсов, еще пометить объект, что ему не нужна файнализация.
Если все делать грамотно, то можно добиться лучшего поведения чем в C++ (разве что там будут использовать свои кучи).
По таймеру вроде сборку мусора не делают. Зачем?
Для объектов в стеке нет деструкторов. Все как раз из-за этих самых исключений. В CLR и в JVM нет оверхеда от исключений. В С++ с ними большие проблемы. Дело доходит до того, что качество оптимизации кода сильно падает там, где их много.
как быть с таким сценарием:
много-много выделяем
потом одних махом удаляем
далее долгое время ничего не делаем
когда будет освобождена память?
На сколько я знаю (могу и ошибаться конечно но в CLR освобождение происходит при необходимости во время выделения (т.е. перед выделением).
Т.о. образом в твоем сценарии сборка еще не произошла.
Да, зануляем ссылки
зы
Выделяли перед этим "связанную" память, например, связанную в линейный список.
При удаление, соответственно, обнуляем ссылку на корень списка.
> но в CLR освобождение происходит при необходимости во время выделения (т.е. перед выделением).
но это не мешает также очищать память и по таймеру.
безразницы
будем считать, что память выделялась связанным образом.
А ты представляешь как это делать Подумай, что нужно при этом сделать, что нужно GC знать про состояние проги. Прогу все равно придется остановить. Так какой смысл это делать в процессе работы проги. Позже прога еще больше объектво освободит. Тогда и соберем все сразу, когда проге нужно будет сделать новую серию выделений памяти. Такое поведение GC более предсказуемое и позволяет делать более определенные оптимизации.
Короче -- ленивая сборка мусора
в чем проблема?
Timer timer = new Timer(delegate(object state) { GC.Collect; }, null,
TimeSpan.Zero, TimeSpan.FromMinutes(10;
void m
{
Data data = DoAlotOfAlloc;
foreach(DataElement e in data) DoProcessing(e);
}
не вижу в твоей программе обнуления ссылок.
void m
{
Data data = DoAlotOfAlloc;
foreach(DataElement e in data)
{
DoProcessing(e);
data.DeleteElement(e); // here we set ref to e in data to null
}
}
А разве таймер не на месагах построен? Т.е. внутри моего метода событие не вызовется.
System.Threading.Timer,
System.Timers.Timer,
System.Windows.Forms.Timer.
Только System.Windows.Forms.Timer основан на windows сообщениях.
остальные таймеры при наступление события вызывают функцию в одном из потоков из thread pool-а.
Ну и как ты будешь делать сборку памяти, когда там в это время прога выполняется.
Как происходит освобождение памяти в многопоточном приложении?
причем, например, в вычислительной или в серверной задаче, которые не приостанавливаются?
Хороший вопрос . Не знаю. Когда GC передвигает объекты в куче, то ему нужно менять значения в том же стеке. Понятно как это делается, когда состояние стека зафиксировано.
Блин и правда не знаю.
class Program
{
static List<int[]> items = new List<int[]>
static void Main(string[] args)
{
// using (new System.Threading.Timer(
// delegate(object state) { GC.Collect; }, null,
// TimeSpan.Zero, TimeSpan.FromSeconds(15
{
for (int i = 0; i < 30; ++i)
{
items.Add(new int[1000000]);
for (int j = 0; j < 1000000; ++j)
items[i][j] = j;
}
items = null;
Console.ReadLine;
}
}
}
Память освободилась через 7 минут, если раскомментировать using, то память освободится через 15 секунд.
Проблема когда больше одного проца
Хорошо, а что с основным потоком происходило? Может попробуй сделать вывод в цикле счетчика времени и насильно сборку 3го поколения.
Скан памяти выполняется отдельным потоком, соответственно на время скана можно приостанавливать все остальные треды, например, через Thread.Suspend
Еще не используй большие массивы (>80кб). Под такие объекты память выделяется в отдельную кучу. И думаю относится она к 3му поколению. Поэтому и не было сборок. Короче без нее бы разобраться
Висел в ReadLine.
> Может попробуй сделать вывод в цикле счетчика времени и насильно сборку 3го поколения.
не понял что ты хочешь сделать, и что ты хочешь в результате увидеть.
От куда инфа, что GC работает в отдельном потоке. Да и не важно. Получается нужно супендать на время всей сборки мусора -- не очень с точки зрения маштабируемости. Штука в том, что в CLR есть две версии GC: десктопный и серверный. Хотя я уже не уверен в чем у них различие.
Что счетчик обрывался. И что GC.Collect(2) из другого потока суспендал основной.
эффект тот же самый
public class Entry
{
public Entry { }
public Entry(Entry next) { this.Next = next; }
int[] data = new int[1000];
public Entry Next;
}
class Program
{
static Entry root;
static void Main(string[] args)
{
// using (new System.Threading.Timer(
// delegate(object state) { GC.Collect; }, null,
// TimeSpan.Zero, TimeSpan.FromSeconds(15
{
for (int i = 0; i < 20000; ++i)
{
root = new Entry(root);
}
root = null;
Console.ReadLine;
}
}
}
И что не было ни одного коллекта, кроме тех, что по таймеру вызываются?
конечно были, во время выделения памяти, но они ведь ничего удалить-то не могут
Полный скан выполняется за довольно маленькое время, соответственно поймать его будет сложно.
на rsdn.ru как-то приводились замеры скорости работы GC.
Согласно Maoni's WebLog сборка производится только в 3х случаях:
1) Allocation exceeds the Gen0 threshold;
2) System.GC.Collect is called;
3) System is in low memory situation;
Большие объекты (>85000) хранятся в отдельной кучи, их сборка происходит во время полной сборки (т.е. вместе с Gen2). Большие объекты не двигают, фактически куча ведет себя как в С.
Смысла большого делать GC.Collect нет. CLR довольно эффективно выбирает момент для GC.Collect(0). Хотя можно сделать GC.Collect(2 если известно, что это освободит большое число старых и/или больших объектов.
1) Workstation GC with Concurrent GC off
2) Workstation GC with Concurrent GC on (for default standalone app)
3) Server GC
в msdn-е есть, например, такой note:
In the .NET Framework version 1.0, the common language runtime (CLR) had a separate memory manager for the large object heap. Under some circumstances this memory manager would not return unused memory to the operating system, and in a few cases would not make the memory available for garbage collection. This resulted in failure to allocate memory due to virtual address space fragmentation. In the .NET Framework 1.1, the large object heap is composed of contiguous areas of memory called heap segments, properly aligned to minimize virtual memory fragmentation. During garbage collection, the space reclaimed from large objects is consolidated and placed in a free list. Heap segments containing only free list items are freed and the memory is returned to the operating system. These changes to the large object heap have effectively eliminated memory allocation failures due to virtual address space fragmentation
Про все.
Для всех вариантов GC и уровеней сбора managed кпотоки останавливаются на всремя сборки. Единственное исключение - это Workstation GC with Concurrent GC on при сборе Gen2. В этом случае на время определенного этапа сбора managed потоки запускаются.
Unmanaged потоки не останавливаются, но с ними проблем нет, т.к. все объекты, которыми они могут пользоваться должны быть pinned.
Есть следующая версия. Потоки останавливать нужно при сборе ссылок из стэка. Потом пока GC бегает по дереву объектов, потоки могут работать. Все выделенные за это время объекты считаюся доступные (т.е. не мусор). Потом на время уплотнения объектов в куче потоки еще раз останавливаются.
http://blogs.msdn.com/maoni/archive/2004/06/15/156626.aspx
Все дело в том, что с системой GC работает целыми сегментами. Для кучи больших объектов используютя отдельные сегменты. Из-за того, что там объекты не уплотняются, вероятность, что сбора освободится сегмент не велика.
Есть такой стиль программирования (в основном системного
когда основной метод освобождения ресурсов - это завершение процесса.
То есть для каждой подзадачи создаётся процесс, по завершению которого
освобождаются ресурсы, связанные с подзадачей.
Естественно, это работает только для тех видов ресурсов, которые относятся к процессам:
память, файловые дескрипторы и некоторые другие.
Так вот, в этом случае отсутствие явного освобождения ресурсов вполне правильно.
Некоторое обобщение такого подхода --- region-based allocators.
CLR по умолчанию не вызывает файналайзы для живых объектов при разрушении процесса.
Что значит по умолчанию?
Как тогда, например, закрываются подсоединения к базе? Дохнут по таймауту со стороны базы?
Ну вроде так, сам не проверял. Там есть какой-то метод GC.XXXOnShutdown который включает такое поведение.
при таком запуске
public class Program
{
public class Xxx
{
public Xxx
{
Console.WriteLine("init");
}
~Xxx
{
Console.WriteLine("done");
}
}
static Xxx xxx = new Xxx;
static void Main{}
}
Program.exe > q
формирует вот такой файл q
init
done
А если в main насильно убивать процесс?
new Timer(delegate(object state) { GC.Collect; }, null,
Это где так красиво, в 2.0?
Ага. Там вроде еще можно было параметр не писать в delegate.
Да, 2.0, фича - анонимные делегаты.
To : можно параметр не писать в delegate?
если не используется, то можно не писать.
Мощно, внушает. Похоже, пора учить 2.0
Оставить комментарий
enochka1145
> ...ведь все равно нужно будет когда-то удалять битмарБлин, XXI век на дворе, а мы всё думаем, когда бы удалить объект!
Представляете, если бы вам 10-20 лет назад нужно было писать так:
{
int a;
...
delete a;
}
Мораль: дуйте на C#/Java, если только бизнес-логика вашего приложения дозволяет не отображать формочки с частотой 30 fps.