Re: Почему GC не освобождает память при выделении больших управляемых массивов?
От: Nikolay_P_I  
Дата: 03.02.14 05:28
Оценка: 9 (1)
Так это давно известная, но малоафишируемая ботва. Я в этой ветке этот эффект еще про FrameWork 1.1 описывал. С тех пор его перманентно костылят, но так как аппетиты разработчиков перманентно же растут — народ перманентно влетает на те же грабли.

Если кратко описать проблему — реальный механизм работы GC не похож на то, что описано в статьях. В одном четком месте — при нехватке памяти GC НЕ начинает СРАЗУ чистить память. GC чистит память по некой эвристике. И иногда банально не успевает. И, в частности, потому вызов GC.Collect — не панацея.

Тестовая программа проста — циклическое выделение массивов все увеличивающегося размера. Причем ссылки на массивы не хранятся и теоретически они собираются GC. На практике же получаем веселые эффекты типа GC.Collect то помогает, то нет, иногда помогает связка GC.Collect + Thread.Sleep(1000), иногда нет. Самый веселый эффект был на FW 1.1, когда массивы средней величины давали OutOfMemory, а большой — нет

Вывод: На разных версиях и разрядностях FW всё по-разному, но работа с частым выделением больших массивов на .NET около границы памяти — опасна.
Re[3]: Почему GC не освобождает память при выделении больших управляемых массиво
От: Sinix  
Дата: 03.02.14 05:29
Оценка:
Здравствуйте, Albeoris, Вы писали:

A>Отличный пример: Encoding.GetChars.

A>И вот как тут быть? Выделять неуправляемую память или использовать управляемые массивы?
Encoding.GetDecoder()?
Re: Почему GC не освобождает память при выделении больших управляемых массивов?
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 03.02.14 07:41
Оценка:
Здравствуйте, Albeoris, Вы писали:

A>Приложение использует .NET Framework, сборка AnyCPU.

A>Последовательная обработка файлов, загружает их полностью в память в виде массивов byte[].
A>Размер файлов колеблется от нескольких килобайт до нескольких мегабайт.
A>После N'го файла (>200) приложение падает с OutOfMemoryException.
A>Почему это происходит?

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

A>Общеизвестные факты:

A>1) Управляемые массивы больше 85000 байт выделяются в большой куче (LOH), которая до 4.5.1 не поддерживала сжатие (Compact), а данные в ней не могли перемещаться, и частое выделение (и удерживание) массивов приводит к её фрагментации.

Если массивы не удерживаются в памяти, то дотнет поосвобождает все что надо. В x32 ты можешь рассчитывать в среднем на 1гб памяти. Если выделять большими блоками, около 1.5, но надо учитывать, что все сборки, данных, UI и тд так же съедает память.

A>У меня есть множество предположений на этот счёт, но они не подкреплены фактами. И главное — я никак не могу вопросизвести ситуацию, в которой получил ошибку. А это очень важно.

A>Если кто-нибудь может дать ответ на этот вопрос — помогите, пожалуйста.

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

A>И связанный с этим вопрос:

A>Если бы у вас возникла необходимость в разработке стабильного и шустрого приложения, которое использует динамические буферы, как в примере выше и не может себе позволить вызывать GC.Collect после обнуления каждого, что бы вы использовали: управляемые массивы или Marshal.AllocHGlobal, обёрнутый в SafeBuffer? Почему? Если не сложно — приведите за и против, которые придут на ум.

Marshal.AllocHGlobal нужен для исключения влияния GC, то есть, вообще. Это нужно для всяких систем, где важны гарантии времени отклика, т.е. просто адский реалтайм и тд и тд.

Если требований реалтайма нет, то надо по максимуму использовать GC. Самое главное — всеми силами бороться с ростом Gen2. Если можно пересоздать объект заново, то так и стоит делать.
Re[2]: Почему GC не освобождает память при выделении больших управляемых массиво
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 03.02.14 07:52
Оценка: -1
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Например, можно использовать не массивы, а так называемые "chunked arrays": буфер поделен на равные части, для которых есть O(1) индекс.

EP>В C++ такая структура данных есть начиная с первого ISO 1998 — std::deque. Аналог для C# разыскивался в том КСВ топике
Автор: Evgeny.Panasyuk
Дата: 21.10.13
, но никакой вменяемой альтернативы знатоками C# продемонстрировано не было. Поэтому один из вариантов — сделать такой chunked array самому.


Без каких либо внятных сведений о наличии-отсутствии конского расхода памяти, утечек, состоянии приложения на момент OOM крайне странно советовать какие то структуры данных.

Что касается chunked array, то ты вопрошал про deque, которая является частным случаем chunked array и собственно в дотнете от неё толку около нуля. Просто варианты chunked array тебе показывали, так шта...
Re[2]: Почему GC не освобождает память при выделении больших управляемых массиво
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 03.02.14 07:55
Оценка:
Здравствуйте, AndrewVK, Вы писали:

A>>1) Управляемые массивы больше 85000 байт выделяются в большой куче (LOH),


AVK>Не в большой куче, а в куче больших объектов


Не в куче больших объектов, а в кучах больших объектов
Re[3]: Почему GC не освобождает память при выделении больших управляемых массиво
От: Evgeny.Panasyuk Россия  
Дата: 03.02.14 17:45
Оценка:
Здравствуйте, Ikemefula, Вы писали:

EP>>Если выделять буферы одинакового размера, то внешняя фрагментация не страшна, даже без компактификации, даже на простейших аллокаторах.

EP>>Например, можно использовать не массивы, а так называемые "chunked arrays": буфер поделен на равные части, для которых есть O(1) индекс.
EP>>В C++ такая структура данных есть начиная с первого ISO 1998 — std::deque. Аналог для C# разыскивался в том КСВ топике
Автор: Evgeny.Panasyuk
Дата: 21.10.13
, но никакой вменяемой альтернативы знатоками C# продемонстрировано не было. Поэтому один из вариантов — сделать такой chunked array самому.

I>Без каких либо внятных сведений о наличии-отсутствии конского расхода памяти, утечек, состоянии приложения на момент OOM крайне странно советовать какие то структуры данных.

Я показал один из вариантов что делать при фрагментации. Фрагментация у ТС (а он её явно упоминал), или что-то иное — это другой вопрос.
Ничего странного тут нет

I>Что касается chunked array, то ты вопрошал про deque, которая является частным случаем chunked array и собственно в дотнете от неё толку около нуля. Просто варианты chunked array тебе показывали, так шта...


Дело было так
Автор: Evgeny.Panasyuk
Дата: 18.10.13
:

EP>>>>>О, кстати. Я спрашивал про аналог C++1998 std::deque — мне пытались впарить LinkedList

под видом деки. Может ты покажешь какую деку обычно используют в .NET — ведь должно же быть что-то стандартное, или распространённое, раз с List'ом такой ужас-ужас.
S>>>>С List проблем нет, как уже не раз говорилось. В дотнете из коробки есть только однонаправленная Queue<T>, если нужен имено дек — есть готовые реализации, например http://nitodeque.codeplex.com/
EP>>>Нужен именно O(1) random-access реализованный на чанках, как и std::deque.
EP>>>По этой ссылке, вроде то что нужно — "This deque provides O(1) indexed access,". Но смущает: "all page views=613" — для такой базовой структуры не слишком надёжно.
S>>Если нужно именно на чанках — есть BigList в PowerCollections, больше ничего на глаза не попадалось, специально не искал.
EP>Вот, уже теплее "all page views=296189". Но судя по тому, что там, как они говорят, эффективные вставки в середину — это не прямой аналог std::deque.
EP>Точно: "Getting or setting an item takes time O(log N), where N is the number of items in the list" против O(1) у std::deque. То есть это, по идее, аналог std::map<std::size_t,T>.

Сначала мне пытались продать linked list под видом chunked array, а потом дерево. Нормальный chunked array никто так и не показал
Re[4]: Почему GC не освобождает память при выделении больших управляемых массиво
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 03.02.14 18:04
Оценка: :)
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Я показал один из вариантов что делать при фрагментации. Фрагментация у ТС (а он её явно упоминал), или что-то иное — это другой вопрос.

EP>Ничего странного тут нет

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

EP>Сначала мне пытались продать linked list под видом chunked array, а потом дерево. Нормальный chunked array никто так и не показал


Доступ по индексу в дотнете никогда не бывает узким местом. Теоретически, возможно, но в большинстве случаев это означает, что задачу надо переписать на С++. То есть, "чтото на чунках" можно слепить как угодно и замедлить доступ по индексу практически до линейной зависимости и это даст только профит. Потому есть всякие BigList, ScanList и очереди всяких сортов, а deque нет.

Есть решения для объектов размером более 2gb, но тут такой фокус, что дотнет совсем недавно выбрался из 2gb АП.
Re[2]: Почему GC не освобождает память при выделении больших управляемых массиво
От: Albeoris  
Дата: 04.02.14 18:05
Оценка:
Здравствуйте, Ikemefula, спасибо, учту.
"Хаос всегда побеждает порядок, поскольку лучше организован." (с) Терри Пратчетт
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.