[C++/Java/C#] Освобождение ресурсов

enochka1145

> ...ведь все равно нужно будет когда-то удалять битмар
Блин, XXI век на дворе, а мы всё думаем, когда бы удалить объект!
Представляете, если бы вам 10-20 лет назад нужно было писать так:
{
int a;
...
delete a;
}
Мораль: дуйте на C#/Java, если только бизнес-логика вашего приложения дозволяет не отображать формочки с частотой 30 fps.

bastii

В C# своих заморочек хватает. Думаешь там тоже таких проблем нет. Ха. Посмотрим как твоя программа будет ползать, если ты не будешь задумываться когда кого разрушают. Так может получится, что для написания .NET проги, которая ведет себя не хуже native варианта, нужно знать еще по более, чем для нормального проганья под MFC.
Подсказка. Если ты не делаешь dispose для своих объектов, то их жизнь очень сильно продлевается. Так время проведенное в GC может в несколько раз вырасти. Советую по играться с CLRProfiler, там можно все очень наглядно увидеть.
Так для информации, есть такой документ Improving .NET Application Performance and Scalability -- всего под 1100 стр.

rosali

когда бы удалить объект!
дуйте на C#/Java
А файлы ты в Яве тоже не закрываешь? Bitmap это ресурс операционной системы, потому что он иногда занимает видеопамять и т.п. Его "удалением" называется соответствующий системный вызов, и это не имеет отношения к менеджменту памяти, который в Яве такой клевый со сборкой мусора там и т.д. Не умничай короче, срацца про языки надо в других тредах. Где кстати тред "MFC - говно"

bastii

"MFC - говно"
О, давайте! MFC - говно, Win32 - прикольно, ура WTL

Dasar

> А файлы ты в Яве тоже не закрываешь?
можно не закрывать - закроется сам.
Битмапы и окна, кстати, тоже можно не закрывать (не уничтожать) - это сделается само.
ps
Так делать (не закрывать ресурсы) - не правильно, но сильно облегчает жизнь новичкам.

bastii

Ага. Только может так получится, что данные не флашнутся.

enochka1145

Да ладно передёргивать, я спутал кусок памяти в куче с объектом Windows. Ну с кем не бывает? Это к тому, что файлы в Java, как ни удивительно, тоже закрывают ручками.

rosali

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

Julie16

Ну, да ты и сам знаешь что я на это могу сказать
А что ты можешь сказать? Мне даже интересно стало

bastii

Если раньше отрисовка не остановится (в gdi ресурсы очень ограничены)

rosali

Сборка мусора обычно стартует тогда, когда израсходован heap. Да, можно насильно, можно наверное по таймеру или еще как-то, но по логике вещей единственной причиной собирать мусор является закончившаяся куча. Финализатор в котором написано закрыть файл вызовется только при сборке мусора. Но открытые файлы - это же ресурс совсем другой природы, следуя той же логике файлы вроде как нужно закрывать, когда не хватает свободных файловых дискрипторов. На такое я бы даже согласился, а вот когда они закрываются от того, что память кончилась... В огороде бузина, а...

Dasar

> Если раньше отрисовка не остановится (в gdi ресурсы очень ограничены)
Это с какой скоростью надо захватывать ресурсы?
ps
начиная с win2k - gdi handle-ов что-то в районе 10000 на процесс

Dasar

> Сборка мусора обычно стартует тогда, когда израсходован heap.
т.е. на 4-ех гигах памяти любая программа будет отжирать 4-га памяти?

rosali

Когда стартуешь jvm ты ей указываешь какого размера heap захватывать. После этого она на нем "топчется". Может есть какая-то эвристика, чтобы она его расширяла, но это уже тема докторской диссертации...

Dasar

Но по таймеру-то он все равно сборку мусора делает, а больше ничего и не надо.

Dasar

> Ага. Только может так получится, что данные не флашнутся.
Да, согласен, но с файлами еще много причин есть - почему их стоит закрывать.
Но вот gdi-шные объекты, на первый взгляд, можно не закрывать.
Конечно, если они не в многократном цикле создаются.

enochka1145

Он скажет, что если сборка мусора не потребовалась (например, приложение уже закрылось то всякие finalize никто вызывать не будет (нафиг надо, вся память и так возвращается системе). Следовательно, если закрытие файла ты делаешь в finalize то файл может и не зактрыться, а значит, не полностью записаться.

enochka1145

Уж не знаю, кто этот ОН (помните анекдот про C2H5(OH)? но надеяться на сборку мусора, когда в куче и так дофига места, не стоит. У Б. Экеля в Thinking in Java (или C#?) есть пример: выделяем память, ждём 10 секунд - и почему-то накакого finalize.

Julie16

Хмммм... А в жаве/сшарпе можно создавать объекты на стеке или с поведением как на стеке(при выходе из области видимости или эксепшене)?

Dasar

> или с поведением как на стеке(при выходе из области видимости или эксепшене)
в C# для этого обычно keyword using используют. в Java-е все намного страшнее, приходится try/finally городить.

6yrop

в C# для этого нужно использовать using

Julie16

Значит я еще долго не буду использовать эти языки... С помощью этого механизма можно так красиво делать fine grained locks

freezer


// на шарпе

public class AutoLocker : IDisposable
{
public AutoLocker
{
// блокируем что надо
}
public void Dispose(bool disposing)
{
// разблокируем
}

public static void Main
{
using(AutoLocker myLock = new AutoLocker
{
// делаем что надо, можем кинуть исключение

}

// а тут myLock уже грохнут
}
}

bastii

Не знаю про Джава (что очень печально но в CLR куча разбита на 3 поколения (в Джава вроде 2, но тут уже от JVM зависит). Нулевое поколение находится в самом верху кучи, т.е. там самые молодые объекты живут. Ее размер ограничен, ограничение очень маленькое, в CLR как-то увязано с размером кэша в проце. Если в ней не хватает места для выделения, то присходит сборка мусора только в рамках нулевого поколения. Сборка мусора второго поколения и третьего происходят реже. Какая там стратегия я не знаю.
Файнализация выполняется в отдельном потоке для объектов, которые были помечены как мусор. При этом пока для них не вополнилась файнализация, память что под них не освобождается. Поэтому GC их двигать.
В Dispose нужно кроме очищения ресурсов, еще пометить объект, что ему не нужна файнализация.
Если все делать грамотно, то можно добиться лучшего поведения чем в C++ (разве что там будут использовать свои кучи).

bastii

По таймеру вроде сборку мусора не делают. Зачем?

bastii

Для объектов в стеке нет деструкторов. Все как раз из-за этих самых исключений. В CLR и в JVM нет оверхеда от исключений. В С++ с ними большие проблемы. Дело доходит до того, что качество оптимизации кода сильно падает там, где их много.

Dasar

> По таймеру вроде сборку мусора не делают. Зачем?
как быть с таким сценарием:
много-много выделяем
потом одних махом удаляем
далее долгое время ничего не делаем
когда будет освобождена память?

bastii

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

bastii

Т.о. образом в твоем сценарии сборка еще не произошла.

Dasar

> Что значит удаляем? Зануляем ссылки?
Да, зануляем ссылки
зы
Выделяли перед этим "связанную" память, например, связанную в линейный список.
При удаление, соответственно, обнуляем ссылку на корень списка.
> но в CLR освобождение происходит при необходимости во время выделения (т.е. перед выделением).
но это не мешает также очищать память и по таймеру.

Dasar

> Т.о. образом в твоем сценарии сборка еще не произошла.
безразницы
будем считать, что память выделялась связанным образом.

bastii

А ты представляешь как это делать Подумай, что нужно при этом сделать, что нужно GC знать про состояние проги. Прогу все равно придется остановить. Так какой смысл это делать в процессе работы проги. Позже прога еще больше объектво освободит. Тогда и соберем все сразу, когда проге нужно будет сделать новую серию выделений памяти. Такое поведение GC более предсказуемое и позволяет делать более определенные оптимизации.

bastii

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

Dasar

> А ты представляешь как это делать
в чем проблема?

Timer timer = new Timer(delegate(object state) { GC.Collect; }, null,
TimeSpan.Zero, TimeSpan.FromMinutes(10;

bastii

и как это поможет во время выполнения метода

void m
{
Data data = DoAlotOfAlloc;
foreach(DataElement e in data) DoProcessing(e);
}

Dasar

не вижу в твоей программе обнуления ссылок.

bastii

Ок.

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
}
}

bastii

А разве таймер не на месагах построен? Т.е. внутри моего метода событие не вызовется.

Dasar

в .Net-е есть три таймера:
System.Threading.Timer,
System.Timers.Timer,
System.Windows.Forms.Timer.
Только System.Windows.Forms.Timer основан на windows сообщениях.
остальные таймеры при наступление события вызывают функцию в одном из потоков из thread pool-а.

bastii

Ну и как ты будешь делать сборку памяти, когда там в это время прога выполняется.

Dasar

> Ну и как ты будешь делать сборку памяти, когда там в это время прога выполняется.
Как происходит освобождение памяти в многопоточном приложении?
причем, например, в вычислительной или в серверной задаче, которые не приостанавливаются?

bastii

Хороший вопрос . Не знаю. Когда GC передвигает объекты в куче, то ему нужно менять значения в том же стеке. Понятно как это делается, когда состояние стека зафиксировано.

bastii

Блин и правда не знаю.

Dasar


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 секунд.

bastii

Проблема когда больше одного проца

bastii

Хорошо, а что с основным потоком происходило? Может попробуй сделать вывод в цикле счетчика времени и насильно сборку 3го поколения.

Dasar

Скан памяти выполняется отдельным потоком, соответственно на время скана можно приостанавливать все остальные треды, например, через Thread.Suspend

bastii

Еще не используй большие массивы (>80кб). Под такие объекты память выделяется в отдельную кучу. И думаю относится она к 3му поколению. Поэтому и не было сборок. Короче без нее бы разобраться

Dasar

> Хорошо, а что с основным потоком происходило?
Висел в ReadLine.
> Может попробуй сделать вывод в цикле счетчика времени и насильно сборку 3го поколения.
не понял что ты хочешь сделать, и что ты хочешь в результате увидеть.

bastii

От куда инфа, что GC работает в отдельном потоке. Да и не важно. Получается нужно супендать на время всей сборки мусора -- не очень с точки зрения маштабируемости. Штука в том, что в CLR есть две версии GC: десктопный и серверный. Хотя я уже не уверен в чем у них различие.

bastii

Что счетчик обрывался. И что GC.Collect(2) из другого потока суспендал основной.

Dasar

> Еще не используй большие массивы (>80кб).
эффект тот же самый

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;
}
}
}

bastii

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

Dasar

> И что не было ни одного коллекта, кроме тех, что по таймеру вызываются?
конечно были, во время выделения памяти, но они ведь ничего удалить-то не могут

Dasar

> Что счетчик обрывался. И что GC.Collect(2) из другого потока суспендал основной.
Полный скан выполняется за довольно маленькое время, соответственно поймать его будет сложно.
на rsdn.ru как-то приводились замеры скорости работы GC.

bastii

Ладно, что известно:
Согласно 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 если известно, что это освободит большое число старых и/или больших объектов.

bastii

3 варианта GC:
1) Workstation GC with Concurrent GC off
2) Workstation GC with Concurrent GC on (for default standalone app)
3) Server GC

Dasar

Про какой версии GC идет речь в weblog-е?
в 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

bastii

Про все.

bastii

Все немного прояснилось.
Для всех вариантов GC и уровеней сбора managed кпотоки останавливаются на всремя сборки. Единственное исключение - это Workstation GC with Concurrent GC on при сборе Gen2. В этом случае на время определенного этапа сбора managed потоки запускаются.
Unmanaged потоки не останавливаются, но с ними проблем нет, т.к. все объекты, которыми они могут пользоваться должны быть pinned.
Есть следующая версия. Потоки останавливать нужно при сборе ссылок из стэка. Потом пока GC бегает по дереву объектов, потоки могут работать. Все выделенные за это время объекты считаюся доступные (т.е. не мусор). Потом на время уплотнения объектов в куче потоки еще раз останавливаются.

bastii

Про эту часть из msdn хорошо написано в
http://blogs.msdn.com/maoni/archive/2004/06/15/156626.aspx
Все дело в том, что с системой GC работает целыми сегментами. Для кучи больших объектов используютя отдельные сегменты. Из-за того, что там объекты не уплотняются, вероятность, что сбора освободится сегмент не велика.

Marinavo_0507

> Так делать (не закрывать ресурсы) - не правильно
Есть такой стиль программирования (в основном системного
когда основной метод освобождения ресурсов - это завершение процесса.
То есть для каждой подзадачи создаётся процесс, по завершению которого
освобождаются ресурсы, связанные с подзадачей.
Естественно, это работает только для тех видов ресурсов, которые относятся к процессам:
память, файловые дескрипторы и некоторые другие.
Так вот, в этом случае отсутствие явного освобождения ресурсов вполне правильно.
Некоторое обобщение такого подхода --- region-based allocators.

bastii

CLR по умолчанию не вызывает файналайзы для живых объектов при разрушении процесса.

Dasar

> CLR по умолчанию не вызывает файналайзы для живых объектов при разрушении процесса.
Что значит по умолчанию?
Как тогда, например, закрываются подсоединения к базе? Дохнут по таймауту со стороны базы?

bastii

Ну вроде так, сам не проверял. Там есть какой-то метод GC.XXXOnShutdown который включает такое поведение.

Dasar

вот такой код

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

bastii

А если в main насильно убивать процесс?

freezer

 new Timer(delegate(object state) { GC.Collect; }, null, 

Это где так красиво, в 2.0?

bastii

Ага. Там вроде еще можно было параметр не писать в delegate.

Dasar

> Это где так красиво, в 2.0?
Да, 2.0, фича - анонимные делегаты.

bastii

To : можно параметр не писать в delegate?

Dasar

> To : можно параметр не писать в delegate?
если не используется, то можно не писать.

freezer

Мощно, внушает. Похоже, пора учить 2.0
Оставить комментарий
Имя или ник:
Комментарий: