.Net и Bitmap не удаляются GC автоматически, когда не осталось памяти

#c# #.net #memory #garbage-collection #bitmap

#c# #.net #память #сборка мусора #bitmap

Вопрос:

Мне интересно, как работает распределение и утилизация памяти, выделенной для растровых изображений в .NET.

Когда я создаю много растровых изображений в циклах в функции и вызываю ее последовательно, это будет работать до тех пор, пока в какой-то момент Bitmap не сможет выделить память, выдавая исключение «Недопустимый параметр» для указанного размера.

Если я вызываю сборщик мусора из while в while, он работает.

С помощью следующего кода вы можете повторно исправить ошибку:

 class BitmapObject {
    public bool Visible {
        get { return enb; }
        set { enb = value; }
    }
    private bool enb;
    private Bitmap bmp;
public BitmapObject(int i, bool en)
{
    enb = en;
    bmp = new Bitmap(i, i);


   }
}

class Pool<T> where T : BitmapObject
{
    List<T> preallocatedBitmaps = new List<T>();
public void Fill() {
    Random r = new Random();
    for (int i = 0; i < 500; i  ) {
        BitmapObject item = new BitmapObject(500, r.NextDouble() > 0.5);
        preallocatedBitmaps.Add(item as T);
    }
}

public IEnumerable<T> Objects
{
    get
    {
        foreach (T component in this.preallocatedBitmaps)
        {
            if (component.Visible)
            {
                yield return (T)component;
            }
        }


     }
    }
}

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
{
    for (int i = 0; i < 10; i   )
    {
        Test();

            // without this it breaks
            //GC.Collect();
            //GC.WaitForPendingFinalizers();
        }

        Console.ReadKey();
    }

    private static void Test() {
        Pool<BitmapObject> pool = new Pool<BitmapObject>();
        pool.Fill();

        for (int i = 0; i < 100; i  )
        {
            var visBitmaps = pool.Objects;
            // do something
        }       
     }
}
  

Комментарии:

1. GC никогда не очищает собственные ресурсы, только сам объект Bitmap. Вы несете ответственность за вызов Dispose() .

2. @Ed, хотя технически это верно, когда GC очищает растровое изображение, завершитель растрового изображения удалит ресурс. Но вы правы, что вам не следует полагаться на это.

3. @Talljoe: Да, вы правы, так и будет.

Ответ №1:

Класс Bitmap неизбежно является тем, в котором вы должны перестать игнорировать существование IDisposable. Это небольшой класс-оболочка вокруг объекта GDI . GDI — это неуправляемый код. Bitmap занимает неуправляемую память. Их много, когда bitmap большой.

Сборщик мусора .NET гарантирует, что неуправляемые системные ресурсы освобождаются с потоком завершения. Проблема в том, что это срабатывает только тогда, когда вы создаете достаточное количество управляемых объектов, чтобы запустить сборку мусора. Это не будет хорошо работать для класса Bitmap, вы можете создать много тысяч таких объектов до того, как заполнится поколение # 0 собранной кучи мусора. У вас закончится неуправляемая память, прежде чем вы сможете добраться туда.

Требуется управлять временем жизни используемых вами растровых изображений. Вызовите метод Dispose (), когда он вам больше не нужен. Это не всегда оптимальное решение, возможно, вам придется пересмотреть свой подход, если у вас просто слишком много живых растровых изображений. 64-разрядная операционная система — следующее решение.

Ответ №2:

Bitmap Класс .NET «инкапсулирует GDI bitmap», это означает, что вы должны вызвать Dispose на Bitmap , когда закончите с ним,

«Всегда вызывайте Dispose перед выпуском последней ссылки на изображение. В противном случае используемые ресурсы не будут освобождены до тех пор, пока сборщик мусора не вызовет метод Finalize объекта Image.»

Комментарии:

1. Это означает, что если некоторые растровые изображения используются в универсальных классах, то универсальные классы должны реализовывать IDisposable, а T должен быть одноразовым для вызова классом?

Ответ №3:

Почему бы вам не использовать using ключевое слово. Просто инкапсулируйте в него свой объект Bitmap, и компилятор гарантирует, что вызван метод Dispose.

Это просто синтаксический ярлык для

 try
{
 ...   
}
finally
{
    ...Dispose();
}
  

Комментарии:

1. потому что bitmap может быть закрытым полем класса, который используется в общем внешнем списке… но я думаю, что IDisposable — это правильный путь…