GC в .NET
От: Чистяков Влад (VladD2 ) Российская Империя www.nemerle.org
Дата: 02.03.06 17:28
Оценка: 1285 (36) +1
Статья:
GC в .NET
Автор(ы): Чистяков Влад (VladD2)
Дата: 14.06.2006
Уже много сказано слов о том, что такое GC, чем он хорош и как лучше его применять. Но, наверно, очень многим хочется знать, как устроен конкретный GC. Данная статья открывает некоторые подробности устройчтва GC в .NET Framework.


Авторы:
Чистяков Влад (VladD2 )

Аннотация:
Уже много сказано слов о том, что такое GC, чем он хорош и как лучше его применять. Но, наверно, очень многим хочется знать, как устроен конкретный GC. Данная статья открывает некоторые подробности устройcтва GC в .NET Framework.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: GC в .NET
От: xexe2  
Дата: 06.03.06 08:09
Оценка:
Многообещающее содержание
может есть не rsdn magazine link а статью?
ну вроде и все:)
Re[2]: GC в .NET
От: VladD2 Российская Империя www.nemerle.org
Дата: 06.03.06 08:36
Оценка: +1 :)
Здравствуйте, xexe2, Вы писали:

X>может есть не rsdn magazine link а статью?


Дык ссылка извесна http://rsdn.ru/Mag/Subscribe/
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: GC в .NET
От: _FRED_ Черногория
Дата: 06.03.06 09:22
Оценка:
Здравствуйте, Чистяков Влад (VladD2 ), Вы писали:

ЧВV>Аннотация:

ЧВV>Уже много сказано слов о том, что такое GC, чем он хорош и как лучше его применять. Но, наверно, очень многим хочется знать, как устроен конкретный GC. Данная статья открывает некоторые подробности устройcтва GC в .NET Framework.

Это статья по мотивам

Вторым выступлением на платформе 2006 был круглый стол, посвященный вопросам производительности, проведенный Владом Чистяковым (VladD2) с участием российских (в том числе и с нашего сайта) и украинских MVP…

здесь
Автор(ы): Купаев Михаил
Дата: 30.12.2005
7-8 декабря в Москве прошла очередная конференция Microsoft – «Платформа 2006». На ней было множество интересных докладов по различной тематике (от технологий управления проектами, до БД и тонких вопросов программирования). Для нас самым интересным в этом году было выступление на этой конференции представителей нашего сайта.
?
... << RSDN@Home 1.2.0 alpha rev. 643>>
Now playing: «Тихо в лесу…»
Help will always be given at Hogwarts to those who ask for it.
Re[2]: GC в .NET
От: VladD2 Российская Империя www.nemerle.org
Дата: 06.03.06 10:23
Оценка: 6 (1)
Здравствуйте, _FRED_, Вы писали:

_FR>Это статья по мотивам

_FR>

_FR>Вторым выступлением на платформе 2006 был круглый стол, посвященный вопросам производительности, проведенный Владом Чистяковым (VladD2) с участием российских (в том числе и с нашего сайта) и украинских MVP…

_FR>здесь
Автор(ы): Купаев Михаил
Дата: 30.12.2005
7-8 декабря в Москве прошла очередная конференция Microsoft – «Платформа 2006». На ней было множество интересных докладов по различной тематике (от технологий управления проектами, до БД и тонких вопросов программирования). Для нас самым интересным в этом году было выступление на этой конференции представителей нашего сайта.
?


Очень отдаленно. В том смысле, что корни идеи растут от туда, но на платформе про ЖЦ реально почти ничего не говорилось.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: GC в .NET
От: Nikolay_P_I  
Дата: 13.03.06 09:20
Оценка:
Здравствуйте, Чистяков Влад (VladD2 ), Вы писали:

ЧВV>Аннотация:

ЧВV>Уже много сказано слов о том, что такое GC, чем он хорош и как лучше его применять. Но, наверно, очень многим хочется знать, как устроен конкретный GC. Данная статья открывает некоторые подробности устройcтва GC в .NET Framework.

А вопросов подкинутть можно ? В том числе и флеймовых ?

1) ConcurrentGC работает начиная только с 1.1SP1 или же, как Рихтер писал — с самого начала ?
1.1) Возможна ли ситуация, когда работающий в параллельном потоке GC не успеет чистить память ?

2) Когда начинается сборка мусора ? Учитывая фразу про "эвристику" ?

3) Как\чем снаружи посмотреть workset ? Память я посмотрю в Task Manager, а workset ?
Надо именно для описываемого случая — посмотреть не стоит ли уменьшить в настройках размеры кэшей для предотвращения свопа.

4) Нельзя ли осветить работу LOH ? Несколько раз поднимал вопрос о "premature Out of Memory Exception", как его обозвали зарубежные коллеги (если быстро выделять и переставать использовать большие объекты типа Char[10Mb+-1Mb] — будет нехватка памяти непонятно почему. Особенно на 1.1 без SP1).
Непонятно — как такое вообще может быть, если GC чистит память КАЖДЫЙ раз, когда ее не хватает. Если там эвристика — тогда понятно, но хотелось бы про нее знать.
Re: GC в .NET
От: Yuri Abele Германия yabele.blogspot.com
Дата: 13.03.06 09:34
Оценка:
Я где-то туплю наверное или это так и надо — вторая половина статьи пока не написана или что?
Я про вот этот кусок:
Использование нескольких процессоров
Concurent GC
Сборщик мусора, основанный на поколениях
Запуск процесса сборки мусора
Сегменты
Контекст размещения
Устройство объекта
Создание и инициализация объектов
Исключения в конструкторе и финализация
Вычисление графа достижимых объектов и корни GC
Алгоритм сканирования графа живых объектов
Корни GC
Барьер записи (write barrier)
Средства обнаружения утечек памяти и контроля за ее распределением
SoS
CLR Profiler
Нехватка памяти
Re[2]: GC в .NET
От: Odi$$ey Россия http://malgarr.blogspot.com/
Дата: 13.03.06 09:51
Оценка:
Здравствуйте, Yuri Abele, Вы писали:

YA>Я где-то туплю наверное


http://gzip.rsdn.ru/Forum/Message.aspx?mid=1711901
Автор: vgrigor
Дата: 03.03.06
Re[3]: GC в .NET
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.03.06 13:43
Оценка: :))
Здравствуйте, Odi$$ey, Вы писали:

OE>http://gzip.rsdn.ru/Forum/Message.aspx?mid=1711901
Автор: vgrigor
Дата: 03.03.06


Ну, ты нашел на что ссылку дать.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: GC в .NET
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.03.06 13:43
Оценка:
Здравствуйте, Yuri Abele, Вы писали:

YA>Я где-то туплю наверное или это так и надо — вторая половина статьи пока не написана или что?


Это анонс статьи. Целеком статья доступна пока только в журнале. На сайт она будет выложена в течении полугода.

В анонсе помещено только введение.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: GC в .NET
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.03.06 13:43
Оценка:
Здравствуйте, Nikolay_P_I, Вы писали:

N_P>1) ConcurrentGC работает начиная только с 1.1SP1 или же, как Рихтер писал — с самого начала ?


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

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


Очень теоритически. На практике это не является проблемой.

N_P>2) Когда начинается сборка мусора ? Учитывая фразу про "эвристику" ?


В статье же сказано:

1. При очередном выделении памяти GC замечает, что превышен размер нулевого поколения.
2. Приложение самостоятельно вызывает метод GC.Collect().
3. Нехватка памяти в ОС.

В нормальных условиях работает в основном только пункт 1.

N_P>3) Как\чем снаружи посмотреть workset ? Память я посмотрю в Task Manager, а workset ?


Task Manager-ом и посмотреть. Если не путаю, колонка "Mem usage" и есть WorkSet.

N_P>Надо именно для описываемого случая — посмотреть не стоит ли уменьшить в настройках размеры кэшей для предотвращения свопа.


Для этого нужно наблюдать за динамикой. В разделе посвященном нехватки памяти как раз об этом говорится. Лучшим инструментом тут является Process Exlorer от www.sysinternals.com.

N_P>4) Нельзя ли осветить работу LOH ?


Да в общем-то про LOH много не скажешь. Но попробовать можно.

N_P> Несколько раз поднимал вопрос о "premature Out of Memory Exception", как его обозвали зарубежные коллеги (если быстро выделять и переставать использовать большие объекты типа Char[10Mb+-1Mb] — будет нехватка памяти непонятно почему. Особенно на 1.1 без SP1).


Это извесная проблема. Вроде как вылечена во втором фрэймворке. Данная ситуация зачастую возникает в следствии фрагментации обычно кучи. Причем обычно это является следствием применения библиотек вроде сокетов. Они делают кучу pin-ов объектов, и при сборке мусора получается сильная фрагментация. Старные алгоритмы неумели разруливать эту ситуацию. Вроде как в 2.0 ввели повторное использование сегментов, что должно было устранить проблему или по крайней мере снизить ее влияние.

Однако и в 1.1 можно было бороться с проблемой выделяя буферы в LOH или насильственно продвигая их во второе поколение.

N_P>Непонятно — как такое вообще может быть, если GC чистит память КАЖДЫЙ раз, когда ее не хватает. Если там эвристика — тогда понятно, но хотелось бы про нее знать.


Память занимается сегментами. Ранее если сегмент второго поколения имел дыры внутри, то они никогда не заполнялись и не уплотнялись. Это могло приводить к ситуации когда при наличии моря памяти в системе ЖЦ нарывался на ее отсуствие (кончалась виртуалка). Выше я об этом уже говорил.

Проявляется ли данная проблема под вторым фрэймворком?
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: GC в .NET
От: Nikolay_P_I  
Дата: 13.03.06 14:44
Оценка:
Здравствуйте, VladD2, Вы писали:

N_P>>1) ConcurrentGC работает начиная только с 1.1SP1 или же, как Рихтер писал — с самого начала ?

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

Понятно. Просто у Рихтера этот параметр в .exe.config рассматривается, когда еще и 1.1 не было.

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

VD>Очень теоритически. На практике это не является проблемой.

Ну и хорошо.

N_P>>2) Когда начинается сборка мусора ? Учитывая фразу про "эвристику" ?

VD>В статье же сказано:
VD>

1. При очередном выделении памяти GC замечает, что превышен размер нулевого поколения.
VD>2. Приложение самостоятельно вызывает метод GC.Collect().
VD>3. Нехватка памяти в ОС.

VD>В нормальных условиях работает в основном только пункт 1.

Угу. С Gen0\1 ясно. Про "эвристику" было относительно сборки в Gen2.

N_P>>3) Как\чем снаружи посмотреть workset ? Память я посмотрю в Task Manager, а workset ?

VD>Task Manager-ом и посмотреть. Если не путаю, колонка "Mem usage" и есть WorkSet.

Ээээ ? А что тогда "память" ?

N_P>>Надо именно для описываемого случая — посмотреть не стоит ли уменьшить в настройках размеры кэшей для предотвращения свопа.

VD>Для этого нужно наблюдать за динамикой. В разделе посвященном нехватки памяти как раз об этом говорится. Лучшим инструментом тут является Process Exlorer от www.sysinternals.com.

Методика понятна — хочется синхронизации в терминах — какие именно колонки где смотреть ?

N_P>>4) Нельзя ли осветить работу LOH ?

VD>Да в общем-то про LOH много не скажешь. Но попробовать можно.

N_P>> Несколько раз поднимал вопрос о "premature Out of Memory Exception", как его обозвали зарубежные коллеги (если быстро выделять и переставать использовать большие объекты типа Char[10Mb+-1Mb] — будет нехватка памяти непонятно почему. Особенно на 1.1 без SP1).


VD>Это извесная проблема. Вроде как вылечена во втором фрэймворке. Данная ситуация зачастую возникает в следствии фрагментации обычно кучи. Причем обычно это является следствием применения библиотек вроде сокетов. Они делают кучу pin-ов объектов, и при сборке мусора получается сильная фрагментация. Старные алгоритмы неумели разруливать эту ситуацию. Вроде как в 2.0 ввели повторное использование сегментов, что должно было устранить проблему или по крайней мере снизить ее влияние.


Простейший пример с цикличным new Char[] все возрастающего объема — не должен иметь pin ? А оно там отлавливается стабильно.

VD>Однако и в 1.1 можно было бороться с проблемой выделяя буферы в LOH или насильственно продвигая их во второе поколение.


А как это ?

N_P>>Непонятно — как такое вообще может быть, если GC чистит память КАЖДЫЙ раз, когда ее не хватает. Если там эвристика — тогда понятно, но хотелось бы про нее знать.

VD>Память занимается сегментами. Ранее если сегмент второго поколения имел дыры внутри, то они никогда не заполнялись и не уплотнялись. Это могло приводить к ситуации когда при наличии моря памяти в системе ЖЦ нарывался на ее отсуствие (кончалась виртуалка). Выше я об этом уже говорил.

Хмм... Не похоже. Я не утверждаю, но впечатление такое, что дело более тонкое — я не мог выделить Char[8307000]-Char[8390000], но Char[16000000] проходил. Кроме того — видимо, была зависимость от скорости — при долговременной работе с большими Char[] (обработка потока файлов) — все было нормально.
Свопа, кстати, не было во всех случаях и MemUsage сбрасывался к 80Мб потом стабильно.

VD>Проявляется ли данная проблема под вторым фрэймворком?


Хмм. Не совсем. С 1.1 пришлось уйти категорически — невозможность выделить 16Мб памяти — это нонсенс в наше время.
В 1.1 SP1 это дело неплохо поправили и граница в районе 100-200Мб. В 2.0 Еще лучше — там около 500Мб — возможно, это уже предел.
Re[4]: GC в .NET
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.03.06 18:30
Оценка:
Здравствуйте, Nikolay_P_I, Вы писали:

N_P>Понятно. Просто у Рихтера этот параметр в .exe.config рассматривается, когда еще и 1.1 не было.


Он введен в сервиспаке. Если Рихтер писал книгу/статью полсе его выхода, то может быть.

N_P>>>2) Когда начинается сборка мусора ? Учитывая фразу про "эвристику" ?

VD>>В статье же сказано:
VD>>

1. При очередном выделении памяти GC замечает, что превышен размер нулевого поколения.
VD>>2. Приложение самостоятельно вызывает метод GC.Collect().
VD>>3. Нехватка памяти в ОС.

VD>>В нормальных условиях работает в основном только пункт 1.

N_P>Угу. С Gen0\1 ясно. Про "эвристику" было относительно сборки в Gen2.


Дык п. 1 на все поколения распростроняется. Инициируется сборка нехваткой места в поколении 0, но сораться может и второе поколение. Все зависит от того будет ли превышен порог для поколения или нет.

VD>>Task Manager-ом и посмотреть. Если не путаю, колонка "Mem usage" и есть WorkSet.


N_P>Ээээ ? А что тогда "память" ?


А память — это неопределенное понятие. Есть виртуальная память. Она так и называется.

Вообще в таск-менеджере есть хэлп в котором все описано:

Memory Usage
In Task Manager, the current working set of a process, in kilobytes. The current working set is the number of pages currently resident in memory. On the Task Manager Processes tab, the column heading is Mem Usage.

Virtual Memory Size
In Task Manager, the amount of virtual memory, or address space, committed to a process.
...


N_P>Методика понятна — хочется синхронизации в терминах — какие именно колонки где смотреть ?


Дык зависит от ситуации. В принципе динамика ворксета уже о многом говорит. Но иногда даже такая информация как количество page fault.
Лучше всего почитать описание процесс-эксплорера и такс-менеджера. Они о многом говорят. Так же если нет понимания, что такое виртуальная память и т.п. имеет смысл почитать Рихтера, но не про дотнет. А про Виндовс.

N_P>Простейший пример с цикличным new Char[] все возрастающего объема — не должен иметь pin ? А оно там отлавливается стабильно.


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

VD>>Однако и в 1.1 можно было бороться с проблемой выделяя буферы в LOH или насильственно продвигая их во второе поколение.


N_P>А как это ?


Ну, чтобы буфер попал в LOH нужно просто выделить его размером более 85000 байт. А чтобы продвинуть во второе поколение — занять его (их) в начале работы программы и вызвать вручную пару сборок мусара.

N_P>Хмм... Не похоже. Я не утверждаю, но впечатление такое, что дело более тонкое — я не мог выделить Char[8307000]-Char[8390000], но Char[16000000] проходил. Кроме того — видимо, была зависимость от скорости — при долговременной работе с большими Char[] (обработка потока файлов) — все было нормально.


С файлами, кстати, такая байда тоже может быть. Любое обращение к неуправяемому API блокирует объект на время вызова. Если вызов по каким-то причинам длится долго, то может появиться описанная проблема. Так что большие буферы нужно занимать заранее и использовать рачительно.

N_P>Свопа, кстати, не было во всех случаях и MemUsage сбрасывался к 80Мб потом стабильно.


Дык если есть дыры, то примерно так и будет. Хотя тут конечно фиг угадашь.

VD>>Проявляется ли данная проблема под вторым фрэймворком?


N_P>Хмм. Не совсем. С 1.1 пришлось уйти категорически — невозможность выделить 16Мб памяти — это нонсенс в наше время.


У нас на сервере занимаются гигабайты памяти. Вопрос в том как это делать.

N_P>В 1.1 SP1 это дело неплохо поправили и граница в районе 100-200Мб. В 2.0 Еще лучше — там около 500Мб — возможно, это уже предел.


Нет таких ограничений. У меня вот такой тест:
using System;
using System.Collections.Generic;
using System.Text;

class Program
{
    static void Main()
    {
        byte[] array = new byte[1024 * 1024 * 1024]; // Занимаем Гиг!!!

        for (int i = 0; i < array.Length; i++)
            array[i] = (byte)(i % byte.MaxValue);

        int sum = 0;
        foreach (byte value in array)
            sum += value;

        Console.WriteLine("Тест прошел успешно :) sum = " + sum);
    }
}

Проходит нормально выводя:
Тест прошел успешно :) sum = -1073747936
Press any key to continue . . .

И это при том, чо в системе в принципе есть только 1 Гиг памяти и в момент запуска теста занято было около 900 метров (т.е. почти все).

Ворксет при этом составляет около 300 метров, а виртуалки занято под гиг. Оно и понятно.

Просто суммарный объем занятой памяти на процесс не может превышать 2 гиг (3 для спец. режима ОС). Но это ограничения не дотнета, а 32-битной ОС.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: GC в .NET
От: Alex57  
Дата: 13.03.06 20:21
Оценка:
Здравствуйте, VladD2, Вы писали:
VD>Тест прошел успешно sum = -1073747936
VD>Press any key to continue . . .

Запуск в среде VS — Exception of type 'System.OutOfMemoryException' was thrown (ОЗУ 1ГБ).
если запускать не в среде то нормально, но не шибко быстро.
Re[5]: GC в .NET
От: Nikolay_P_I  
Дата: 14.03.06 11:02
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Дык зависит от ситуации. В принципе динамика ворксета уже о многом говорит. Но иногда даже такая информация как количество page fault.

VD>Лучше всего почитать описание процесс-эксплорера и такс-менеджера. Они о многом говорят. Так же если нет понимания, что такое виртуальная память и т.п. имеет смысл почитать Рихтера, но не про дотнет. А про Виндовс.

То есть — я правильно понял, что индикатором о недостатке памяти служит ворксет, который периодически пытается раздуться до размера виртуалки, а потом уменьшается до десятка Мб ? Имеет при этом значение размера виртуальной памяти само по-себе ? 2Гб виртуалки при 1Гб ОЗУ — тут ясно, а если виртуалки 300 Мб ?

N_P>>Простейший пример с цикличным new Char[] все возрастающего объема — не должен иметь pin ? А оно там отлавливается стабильно.


VD>Где там? Конечно если занимать огромные массивы, то рано или поздно можно выйти на момент когда памяти не хватит.


Почему ? Каков механизм ? Если не держать их, а отдавать. Пусть не будет сдвигания объектов, но дефрагментация-то будет работать ?

VD>Но это слишком искуственная ситуация. Массивы большого объема нужно или вообще не занимать,


>85кб на сегодняшний день — это никак не "массив большого объема"


N_P>>Хмм... Не похоже. Я не утверждаю, но впечатление такое, что дело более тонкое — я не мог выделить Char[8307000]-Char[8390000], но Char[16000000] проходил. Кроме того — видимо, была зависимость от скорости — при долговременной работе с большими Char[] (обработка потока файлов) — все было нормально.

VD>С файлами, кстати, такая байда тоже может быть. Любое обращение к неуправяемому API блокирует объект на время вызова. Если вызов по каким-то причинам длится долго, то может появиться описанная проблема. Так что большие буферы нужно занимать заранее и использовать рачительно.

Там из файла только в начале в буфер читается. Средний размер 100кб-1Мб. Но может быть 1к-100Мб. Куча работ со строками и под-буферами.
Например, сторонняя библиотека хотит Byte[] материала с 2000 по end-5000 адрес входной информации. Выделяем — вот и еще 1 большой массив наклюнулся.

N_P>>Хмм. Не совсем. С 1.1 пришлось уйти категорически — невозможность выделить 16Мб памяти — это нонсенс в наше время.

VD>У нас на сервере занимаются гигабайты памяти. Вопрос в том как это делать.

А как можно занять гигабайтЫ ? Интересно с практической точки зрения.

N_P>>В 1.1 SP1 это дело неплохо поправили и граница в районе 100-200Мб. В 2.0 Еще лучше — там около 500Мб — возможно, это уже предел.

VD>Нет таких ограничений. У меня вот такой тест:

Ха! Хитрый какой! Ты не 1 раз этот гигабайт выделяй, а в динамике:

VD>
VD>using System;
VD>using System.Collections.Generic;
VD>using System.Text;

class Program
{
    static void Main()
    {
           Int32 bs = 0;
           try
           {
              while(true)
              {
                 bs = bs + 10; //для 1.1 без SP1 - лучше 10, для прочих - 100
             byte[] array = new byte[bs * 1024 * 1024]; // Занимаем!!!
              };
           }
           catch
           {
          Console.WriteLine(bs);
           }
    }
}


У меня Out of Memory на 230. Вопрос — а ПОЧЕМУ ? Ладно, не происходит сдвига объектов, но дефрагментация куда делась ?

P.S. Виртуальная память — совсем маленькая по Task Manager
Re[6]: GC в .NET
От: VladD2 Российская Империя www.nemerle.org
Дата: 14.03.06 12:23
Оценка:
Здравствуйте, Alex57, Вы писали:

A>если запускать не в среде то нормально, но не шибко быстро.


А что же ты хотел? На свопие сидишь. Погляди сколько у тебя физической памяти свободно. Измени размер массива чтобы он влезал в свободную память и все начнет летать.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: GC в .NET
От: VladD2 Российская Империя www.nemerle.org
Дата: 14.03.06 12:23
Оценка:
Здравствуйте, Nikolay_P_I, Вы писали:

N_P>То есть — я правильно понял, что индикатором о недостатке памяти служит ворксет, который периодически пытается раздуться до размера виртуалки, а потом уменьшается до десятка Мб ?


Это как бы вернейший признак проблем. Но бывает, что не все так очевидно.

N_P> Имеет при этом значение размера виртуальной памяти само по-себе ?


Конечно. Если размер виртуалки приближается к объему физически установленной памяти, то проблемы практически гарантированны.

N_P> 2Гб виртуалки при 1Гб ОЗУ — тут ясно, а если виртуалки 300 Мб ?


Не понял вопроса.

N_P>Почему ? Каков механизм ? Если не держать их, а отдавать. Пусть не будет сдвигания объектов, но дефрагментация-то будет работать ?


Ну, массив в 3 гига занять в принципе невозможно. И надо учитывать, что память расходуется не толкьо под хипы ЖЦ. Она еще идет на внтуренние структуры, на джит... Вожно с помощью SoS и отладчика поглядеть раскладку памяти. Информация не очень понятна, но если разобраться, то все будет очевидно.

N_P>Там из файла только в начале в буфер читается. Средний размер 100кб-1Мб.


Это фигня.

N_P> Но может быть 1к-100Мб. Куча работ со строками и под-буферами.


А вот тут лучше читать через потоки.

N_P>Например, сторонняя библиотека хотит Byte[] материала с 2000 по end-5000 адрес входной информации. Выделяем — вот и еще 1 большой массив наклюнулся.


Он часто нужно? Может занять его один раз в начале пработы приложения?

N_P>А как можно занять гигабайтЫ ? Интересно с практической точки зрения.


Да просто. У нас на сервере делается дифф огномного файла. Сумарный объем занимаемой памяти приложением вылезает за гиг. На сервере 3 гига физической памяти и энтерпрайзная Вынь2003 с ключиком для подержки 3 гиг на процесс.

N_P>Ха! Хитрый какой! Ты не 1 раз этот гигабайт выделяй, а в динамике:


VD>>
VD>>using System;
VD>>using System.Collections.Generic;
VD>>using System.Text;

N_P>class Program
N_P>{
N_P>    static void Main()
N_P>    {
N_P>           Int32 bs = 0;
N_P>           try
N_P>           {
N_P>              while(true)
N_P>              {
N_P>                 bs = bs + 10; //для 1.1 без SP1 - лучше 10, для прочих - 100
N_P>             byte[] array = new byte[bs * 1024 * 1024]; // Занимаем!!!
N_P>              };
N_P>           }
N_P>           catch
N_P>           {
N_P>          Console.WriteLine(bs);
N_P>           }
N_P>    }
N_P>}
N_P>


N_P>У меня Out of Memory на 230. Вопрос — а ПОЧЕМУ ? Ладно, не происходит сдвига объектов, но дефрагментация куда делась ?


Ну, это вообще не ясно что за код. В релизе думаю локальную переменную просто выкинут. Так что унжно код писать как-то так:
using System;
using System.Collections.Generic;
using System.Text;

class Program
{
    static void Main()
    {
        int bs = 1;

        try
        {
            while (bs > 0)
            {
                bs += 10; //для 1.1 без SP1 - лучше 10, для прочих - 100
                _array = null;
                _array = new byte[bs * 1024 * 1024]; // Занимаем!!!
            };

            Console.WriteLine("Усе ОК!");
        }
        catch (Exception ex)
        {
            Console.WriteLine(bs);
            Console.WriteLine(ex.Message);
        }
    }

    static byte[] _array;
}


Этот вариант у меня показывает 1401.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: GC в .NET
От: Nikolay_P_I  
Дата: 14.03.06 14:11
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, Nikolay_P_I, Вы писали:


N_P>> Имеет при этом значение размера виртуальной памяти само по-себе ?

VD>Конечно. Если размер виртуалки приближается к объему физически установленной памяти, то проблемы практически гарантированны.
N_P>> 2Гб виртуалки при 1Гб ОЗУ — тут ясно, а если виртуалки 300 Мб ?
VD>Не понял вопроса.

Совместно с вышеприведенным — то есть в случае, когда виртуалка подходит к размеру ОЗУ — тут и так понятно — памяти не хватает.
Вопрос — а если виртуалки еще меньше, чем ОЗУ, но есть и прочие программы — как узнать — конкретно нам уже не хватает памяти или нет ?
По динамике изменения workset ?

N_P>>Там из файла только в начале в буфер читается. Средний размер 100кб-1Мб.

VD>Это фигня.

Ага. Но уже >85к и LOH.

N_P>> Но может быть 1к-100Мб. Куча работ со строками и под-буферами.

VD>А вот тут лучше читать через потоки.

Обработка НЕпотоковая.

N_P>>Например, сторонняя библиотека хотит Byte[] материала с 2000 по end-5000 адрес входной информации. Выделяем — вот и еще 1 большой массив наклюнулся.

VD>Он часто нужно? Может занять его один раз в начале пработы приложения?

Никто не знает. Встретился ключ — отдай порцию инфы на обработку.

N_P>>А как можно занять гигабайтЫ ? Интересно с практической точки зрения.

VD>Да просто. У нас на сервере делается дифф огномного файла. Сумарный объем занимаемой памяти приложением вылезает за гиг. На сервере 3 гига физической памяти и энтерпрайзная Вынь2003 с ключиком для подержки 3 гиг на процесс.

Ааа... Слышал про это — я думал — программно извратились

N_P>>Ха! Хитрый какой! Ты не 1 раз этот гигабайт выделяй, а в динамике:


VD>>>
VD>>>using System;
VD>>>using System.Collections.Generic;
VD>>>using System.Text;

N_P>>class Program
N_P>>{
N_P>>    static void Main()
N_P>>    {
N_P>>           Int32 bs = 0;
N_P>>           try
N_P>>           {
N_P>>              while(true)
N_P>>              {
N_P>>                 bs = bs + 10; //для 1.1 без SP1 - лучше 10, для прочих - 100
N_P>>             byte[] array = new byte[bs * 1024 * 1024]; // Занимаем!!!
N_P>>              };
N_P>>           }
N_P>>           catch
N_P>>           {
N_P>>          Console.WriteLine(bs);
N_P>>           }
N_P>>    }
N_P>>}
N_P>>


N_P>>У меня Out of Memory на 230. Вопрос — а ПОЧЕМУ ? Ладно, не происходит сдвига объектов, но дефрагментация куда делась ?

VD>Ну, это вообще не ясно что за код. В релизе думаю локальную переменную просто выкинут. Так что унжно код писать как-то так:

Я не подумал — однако в Release локальную не выкинули — я же проверял перед посылкой.

VD>
VD>using System;
VD>using System.Collections.Generic;
VD>using System.Text;

VD>class Program
VD>{
VD>    static void Main()
VD>    {
VD>        int bs = 1;

VD>        try
VD>        {
VD>            while (bs > 0)
VD>            {
VD>                bs += 10; //для 1.1 без SP1 - лучше 10, для прочих - 100
VD>                _array = null;
VD>                _array = new byte[bs * 1024 * 1024]; // Занимаем!!!
VD>            };

VD>            Console.WriteLine("Усе ОК!");
VD>        }
VD>        catch (Exception ex)
VD>        {
VD>            Console.WriteLine(bs);
VD>            Console.WriteLine(ex.Message);
VD>        }
VD>    }

VD>    static byte[] _array;
VD>}
VD>


VD>Этот вариант у меня показывает 1401.


1000 на 2.0
230 на 1.1 SP1
1.1 без SP под рукой нет, но там было-бы что-то типа 20.

Вопрос-то в чем ? То, что в 2.0 это поправлено — я знаю. Вопрос в том, что непонятно, как на 1.1 SP1 получается такой результат.

Возвращаясь к статье — не выходит описанной простоты. Явно ведь есть еще какой-то хитрый механизм.
Re[8]: GC в .NET
От: VladD2 Российская Империя www.nemerle.org
Дата: 16.03.06 06:08
Оценка:
Здравствуйте, Nikolay_P_I, Вы писали:

N_P>Вопрос — а если виртуалки еще меньше, чем ОЗУ, но есть и прочие программы — как узнать — конкретно нам уже не хватает памяти или нет ?


Я не понимаю что значит "если виртуалки еще меньше".

N_P>По динамике изменения workset ?


Ну очень большие скачки workset-а — это точно не хорошо. Хотя сами скачки могут вызваться просто активизацией некого охочего до памяти алгоритма.

N_P>Ага. Но уже >85к и LOH.


Дык это же довольно быстро происходит. Если буферв не удерживаются долго, то ЖЦ их и не заметит. При сборке второго поколения их не станет.

Вот если между этими временными буферами занимается по 90 кил долгоживущих объектов, то это может привести к фрагментации LOH-а. Такого лучше избегать.

N_P>>> Но может быть 1к-100Мб. Куча работ со строками и под-буферами.

VD>>А вот тут лучше читать через потоки.

N_P>Обработка НЕпотоковая.


Конкретнее, если можно. Что мешает ей быть потоковой?

N_P>Никто не знает. Встретился ключ — отдай порцию инфы на обработку.


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

N_P>Ааа... Слышал про это — я думал — программно извратились


Смысла нет. И так дифф жрет реально больше чем следовало бы если заниматься подбором оптимальных значений буферов.

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

VD>>Ну, это вообще не ясно что за код. В релизе думаю локальную переменную просто выкинут. Так что унжно код писать как-то так:


N_P>Я не подумал — однако в Release локальную не выкинули — я же проверял перед посылкой.


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

N_P>Вопрос-то в чем ? То, что в 2.0 это поправлено — я знаю. Вопрос в том, что непонятно, как на 1.1 SP1 получается такой результат.


Точно без наличия исходников это не установишь. Так что просто переходите на 2.0 и забывайте про 1.х к чертям.

N_P>Возвращаясь к статье — не выходит описанной простоты. Явно ведь есть еще какой-то хитрый механизм.


Хм. Простоты вообще добиться сложно. Особенно в сложных вещах. Но это уже философия.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[9]: GC в .NET
От: Nikolay_P_I  
Дата: 16.03.06 07:51
Оценка:
N_P>>Вопрос — а если виртуалки еще меньше, чем ОЗУ, но есть и прочие программы — как узнать — конкретно нам уже не хватает памяти или нет ?
VD>Я не понимаю что значит "если виртуалки еще меньше".

Виртуальной памяти под данную программу занято менее объема ОЗУ, виртуальной памяти вообще всеми программами занято менее объема ОЗУ, workset нашей программы менее виртуальной памяти под нашу программу в пару раз и скачет +-20%.

N_P>>Ага. Но уже >85к и LOH.

VD>Дык это же довольно быстро происходит. Если буферв не удерживаются долго, то ЖЦ их и не заметит. При сборке второго поколения их не станет.
VD>Вот если между этими временными буферами занимается по 90 кил долгоживущих объектов, то это может привести к фрагментации LOH-а. Такого лучше избегать.

"Долгоживущих" здесь имеется ввиду — тоже больших, которые в LOH? А ежели и они потом соберутся так как не нужны ?
В смысле — работа циклическая и после того, как очередной файл обработан — никакие созданные при обработке предыдущего файла объекты и буфера не нужны, кроме нескольких новых объектов в очереди лога, которые долгоживущие, но маленькие.

N_P>>>> Но может быть 1к-100Мб. Куча работ со строками и под-буферами.

VD>>>А вот тут лучше читать через потоки.
N_P>>Обработка НЕпотоковая.
VD>Конкретнее, если можно. Что мешает ей быть потоковой?

Тип фрагмента (и что с ним делать соответственно) — я знаю только после его конца. (Читать "задом-на-перед" не предлагать).
Да и не поможет — я-ж пишу — библиотека стронняя хочет не поток, а массив на входе. И вернет, кстати, тоже массив, но иной длинны. Максимум что я могу — буфер выделить из прочитанного, а потом его в библиотеку отдать. Разницы, если я еще и файл сначала в еще один буфер загоню — почти никакой. Зато обработка легче — вместо автомата на поток — просто регексы.

N_P>>Никто не знает. Встретился ключ — отдай порцию инфы на обработку.

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

Постоянно. Там доморощенный изврат на тему MIME. Так что может быть и 2000 кусков по 1Мб и 1 кусок на 200Мб.

VD>>>Ну, это вообще не ясно что за код. В релизе думаю локальную переменную просто выкинут. Так что унжно код писать как-то так:

N_P>>Я не подумал — однако в Release локальную не выкинули — я же проверял перед посылкой.
VD>Я вот, например, изменил в твоем коде пару строк и получил моментальную отработку. Явно компилятор выкинул бессмысленный код.

Я получал Out of Memory на 220 границы в 1.1 SP1 — не мог он выкинуть в моем случае создание массива.

N_P>>Вопрос-то в чем ? То, что в 2.0 это поправлено — я знаю. Вопрос в том, что непонятно, как на 1.1 SP1 получается такой результат.

VD>Точно без наличия исходников это не установишь. Так что просто переходите на 2.0 и забывайте про 1.х к чертям.
N_P>>Возвращаясь к статье — не выходит описанной простоты. Явно ведь есть еще какой-то хитрый механизм.
VD>Хм. Простоты вообще добиться сложно. Особенно в сложных вещах. Но это уже философия.

Тебе-то смешно... Это теперь, с появлением 2.0 он филосовский. А представь — мы выбирали пару лет назад единую платформу под проект, заинтересовались NET, упирая в первую очередь на надежность, начитались умных книжек типа Рихтера и таких вот статей, где с CG все просто и хорошо — как памяти не хватает, так ее ОБЯЗАТЕЛЬНО очистят, соберут, подвинут или в крайнем случае дефрагментируют, решили смириться с частым GC в обмен на простоту и универсальность алгоритма,сделали работу, поставили на стенд и получили Out of Memory на 16Мб кусках в динамике. Был тогда у нас 1.1 без SP.

Начали выяснять ситуацию и оказалось, что умные книги и статьи, видимо, несколько УПРОЩАЮТ и все несколько не так. К счастью — по ТЗ в динамике нам выделать можно и не более 200Мб, так что 1.1 SP1 нас спас. Прикинь, что были-бы требования до 500Мб. 2.0 еще не было.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.