Из-за нехватки памяти из-за латентности неуправляемой памяти?

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

Я воспроизвел проблему с помощью этого простого кода:

  for (int i = 0; i < 100000; i++)
    var bmp = new RenderTargetBitmap(256, 256, 96, 96, PixelFormats.Default);

Теоретически этот код не должен падать, потому что битмапы должны автоматически собираться с мусором, но при сбое в 32-разрядном режиме он последовательно сработает.

Проблема может быть устранена следующим образом:

  for (int i = 0; i < 100000; i++)
  {
    var bmp = new RenderTargetBitmap(256, 256, 96, 96, PixelFormats.Default);
    if (i % 500 == 0)
    {
      GC.Collect();
      GC.WaitForPendingFinalizers();
    }
  }

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

Кто-нибудь может рассказать об этом? Есть ли лучший способ решить проблему?

Ответы

Ответ 1

RenderTargetBitmap скорее всего, имеет собственный ресурс (ы), связанные с ним. У вас много управляемой памяти (GC получает вызов каждого X-байта) - для управляемых объектов, вероятно, недостаточно памяти, чтобы быть интересными вообще. Таким образом, это должна быть неуправляемая часть - я ожидаю, что она имеет текстуру DirectX (или что-то подобное), лежащую в основе, которая будет выпущена только при выполнении финализаторов.

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

Странно, что RenderTargetBitmap не является IDisposable. Это означает, что вы не можете правильно распоряжаться собственными ресурсами как можно скорее. Таким образом, это больше похоже на ошибку в WPF, чем на сам .NET.

Это просто предположение.

Чтобы обратиться к комментарию, GC определенно не ждет, когда метод выйдет первым. Замена RenderTargetBitmap на byte[] показывает, что это корректно работает, когда не задействованы собственные ресурсы.

EDIT: мне наконец удалось найти это в исходном коде BCL. Чтобы избавиться от собственных ресурсов RenderTargetBitmap, вы должны вызвать Clear. Он будет освобожден, в конце концов, даже без этого (собственные ресурсы находятся на безопасном дескрипторе), но если вы только выделяете и освобождаете RenderTargetBitmap, у вас закончится текстура/родная память задолго до того, как вы получите GC бежать. Поэтому, чтобы ответить на ваш реальный вопрос, просто назовите Clear на растровое изображение, когда он больше не нужен, и он больше не должен перегружать память.

Июль 2015:

Похоже, что исходная ошибка исправлена ​​- глядя через источники 4.5.2, давление памяти правильно применяется, и выделение тонн RenderTargetBitmap должно теперь заставить GC собираться должным образом. Тем не менее, реализация IDisposable.