Загрузка картинок и освобождение памяти
Не могу понять в чем проблема - следующий код работает нормально, до определенного момента, затем генерирует исключение нехватки памяти (OutOfMemoryException). Исключение перехватывается - а как память вернуть системе? Или по крайней мере позволить приложению с ней работать?
try
{
Bitmap tmpBitmap = new Bitmap(odMain.FileName);
pbMain.SizeMode = PictureBoxSizeMode.StretchImage;
pbMain.Image = (Image)tmpBitmap;
lbMain.Visible = false;
}
catch (OutOfMemoryException ex) {
?????//Как получить память обратно здесь?
return;
}
}
}
}
Т.е.
{
private static Bitmap tmpBitmap;
...
private void pbMain_Click(object sender, EventArgs e)
{
if (odMain.ShowDialog()== DialogResult.OK) {
try
{
if (tmpBitmap!=null) { tmpBitmap.Dispose(); tmpBitmap = null; }
tmpBitmap = new Bitmap(odMain.FileName);
pbMain.SizeMode = PictureBoxSizeMode.StretchImage;
pbMain.Image = (Image)tmpBitmap;
lbMain.Visible = false;
}
catch (OutOfMemoryException ex)
{
return;
}
}
}
...
}
ну и естественно надо делать освобождение ресурса там где он уже не нужен.
а если так?
pbMain.SizeMode = PictureBoxSizeMode.StretchImage;
pbMain.Load(odMain.FileName);
...
а если так?
pbMain.SizeMode = PictureBoxSizeMode.StretchImage;
pbMain.Load(odMain.FileName);
...
хм. Ну для создания отдельного объекта есть свои причины - а вот откуда у компонента PictureBox взялся этот метод?
http://msdn.microsoft.com/en-us/library/f6ak7was.aspx
кстати тогда может быть так сделать, если нужно еще как-то обрабатывать изоражение перед загрузкой?
{
Bitmap tmpBitmap = new Bitmap(odMain.FileName);
//... тут еще что-то делаем с битмапом
pbMain.SizeMode = PictureBoxSizeMode.StretchImage;
try { pbMain.Image.Dispose(); }
catch { }
pbMain.Image = tmpBitmap;
}
А вообще странно... Неуправляемый ресурс должен освобождаться как минимум при сборке мусора, а в случае, когда память на исходе, сборщик мусора по-любому должен сработать. Т. е. твое решение, в принципе, должно быть эквивалентно вот такому:
Bitmap bitmap = new Bitmap(path);
pictureBox.Image = bitmap;
Но другое странно: как это памяти остаётся меньше чем на картинку?
не поддерживается. Я указал целевую платформу.
кстати тогда может быть так сделать, если нужно еще как-то обрабатывать изоражение перед загрузкой?
так нельзя. Как раз так и нельзя делать - выделение памяти в данном случае может закончится крахом вне зависимости от самого приложения - это мобильное устройство и существует масса факторов которые могут привести к тому, что создание объекта сгенерирует исключение.
Но другое странно: как это памяти остаётся меньше чем на картинку?
Данное исключение генерируется при невозможности выделить память одним фрагментом - т.е. память на самом деле есть - просто нет возможности в данный текущий момент выделить именно столько непрерывной памяти. Т.е. если я допустим получил исключение - а затем загрузил картинку меньшего размера - то я смогу затем загрузить и нужную мне картинку. Т.е. "сборщик мусора" отрабатывает и дефрагментация памяти происходит. Просто я не могу управлять этим процессом - вот в чем вопрос :) Возможно в C# это просто не возможно сделать - хз. я еще не разобрался.
Код 1:
{
lbList.Items.Add("Memory 1" + GC.GetTotalMemory(false).ToString());
if (odMain.ShowDialog()== DialogResult.OK) {
if (tmpBitmap!=null) { tmpBitmap.Dispose(); tmpBitmap = null; }
try
{
tmpBitmap = new Bitmap(odMain.FileName);
pbMain.SizeMode = PictureBoxSizeMode.StretchImage;
pbMain.Image = (Image)tmpBitmap;
lbMain.Visible = false;
}
catch (OutOfMemoryException ex)
{
System.GC.Collect();
return;
}
finally {
lbList.Items.Add("Memory 1 " + GC.GetTotalMemory(false).ToString());
}
}
}
Код 2:
{
lbList.Items.Add("Memory 2" + GC.GetTotalMemory(false).ToString());
if (odMain.ShowDialog() == DialogResult.OK)
{
try
{
Bitmap tmpBitmap2 = new Bitmap(odMain.FileName);
pbMain.SizeMode = PictureBoxSizeMode.StretchImage;
pbMain.Image = (Image)tmpBitmap2;
lbMain.Visible = false;
}
catch (OutOfMemoryException ex)
{
System.GC.Collect();
return;
}
finally {
lbList.Items.Add("Memory 2" + GC.GetTotalMemory(false).ToString());
}
}
}
Результат работы первого кода:
Memory1 6388
Memory1 537668
Memory1 539278
Memory1 529689
Memory1 532710
Memory1 596809
....
результат работы второго:
Memory2 599020
Memory2 538084
Memory2 538512
Memory2 7048
Memory2 7468
Memory2 7160
<<<<<<<<<<<На следующем шаге уже возникло исключение
Memory2 7580
Memory2 7272
Memory2 7580 << Опять пытаюсь открыть - и опять исключение
Memory2 7272
<<<<<<<< А вот здесь открыта маленькая картинка, для нее памяти хватило
Memory2 7804
Memory2 402988
....
На самом деле он нормально работает - и работает именно так как надо.
Дело в том что в первом коде память возвращается в систему, потому что переменная объявлена за пределами функции - мы имеем возможность "обнулить" указатель, и перед вызовом функции new происходит очистка памяти.
Почему же во втором случае не так? Да элементарно. Объект который, как мы считаем должен быть очищен, на самом деле не подпадает под критерии очистки! На него есть как минимум одна ссылка - на него ссылается поле класса PaintBox! И поэтому память не возвращается в систему. А когда нормально произошел вызов функции new - то тогда (и только тогда) предыдущая картинка становится мусором - ведь Image уже указывает на другой объект. Поэтому в данном случае предпочтительнее объявить указатель за пределами функции - или пытаться освободить объект через Image (я так думаю что это должно сработать - хотите попробуйте)