我有一个应用程序(WPF),可以创建大量的BitmapImages(如25000)。似乎框架使用了一些内部逻辑,因此在创建之后大约消耗300 MB的内存(150个虚拟内存和150个物理内存)。这些BitmapImages将添加到Image对象中,并将它们添加到Canvas中。问题是,当我释放所有这些图像时,内存不会被释放。我怎样才能释放记忆?
应用程序很简单: XAML
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Canvas x:Name="canvas" Grid.ColumnSpan="2"></Canvas>
<Button Content="Add" Grid.Row="1" Click="Button_Click"/>
<Button Content="Remove" Grid.Row="1" Grid.Column="1" Click="Remove_click"/>
</Grid>
代码隐藏
const int size = 25000;
BitmapImage[] bimages = new BitmapImage[size];
private void Button_Click(object sender, RoutedEventArgs e)
{
var paths = Directory.GetFiles(@"C:\Images", "*.jpg");
for (int i = 0; i < size; i++)
{
bimages[i] = new BitmapImage(new Uri(paths[i % paths.Length]));
var image = new Image();
image.Source = bimages[i];
canvas.Children.Add(image);
Canvas.SetLeft(image, i*10);
Canvas.SetTop(image, i * 10);
}
}
private void Remove_click(object sender, RoutedEventArgs e)
{
for (int i = 0; i < size; i++)
{
bimages[i] = null;
}
canvas.Children.Clear();
bimages = null;
GC.Collect();
GC.Collect();
GC.Collect();
}
这是添加图像后ResourceManager的屏幕截图
答案 0 :(得分:6)
Wpf中有一个错误,我们被BitmapImage对象释放的地方所咬,除非你冻结它们。 http://blogs.msdn.com/b/jgoldb/archive/2008/02/04/finding-memory-leaks-in-wpf-based-applications.aspx是我们发现问题的原始页面。它应该在Wpf 3.5 sp1中修复,但在某些情况下我们仍然可以看到它。尝试更改这样的代码以查看是否存在问题:
bimages[i] = new BitmapImage(new Uri(paths[i % paths.Length]));
bimages[i].Freeze();
我们现在经常冻结我们的BitmapImage对象,因为我们在Profiler中看到其他实例,其中Wpf正在侦听BitmapImage上的事件,从而使图像保持活动状态。
如果Feeze()调用对您的代码不是一个明显的修复,我强烈建议您使用一个分析器,例如RedGate Memory Profiler - 它将跟踪一个依赖树,它会告诉您它是什么让您保持内存中的图像对象。
答案 1 :(得分:2)
对我有用的是:
每张图片的清理方法最终都是这样简单:
img.Source = null;
UpdateLayout();
我能够通过实验来实现这一点,方法是保持一个WeakReference()对象的列表指向我创建的每个BitmapImage,然后检查WeakReferences上的IsAlive字段后应该清理它们以确认他们实际上已经被清理干净了。
所以,我的BitmapImage创建方法如下所示:
var bi = new BitmapImage();
using (var fs = new FileStream(pic, FileMode.Open))
{
bi.BeginInit();
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.StreamSource = fs;
bi.EndInit();
}
bi.Freeze();
weakreflist.Add(new WeakReference(bi));
return bi;
答案 2 :(得分:2)
我按照AAAA给出的答案。导致内存填满的原始代码是:
if (overlay != null) overlay.Dispose();
overlay = new Bitmap(backDrop);
Graphics g = Graphics.FromImage(overlay);
使用System.Threading插入AAAA的代码块,C#add&#34;&#34;和VB添加&#34; Imports System.Threading&#34;:
if (overlay != null) overlay.Dispose();
//--------------------------------------------- code given by AAAA
Thread t = new Thread(new ThreadStart(delegate
{
Thread.Sleep(500);
GC.Collect();
}));
t.Start();
//-------------------------------------------- \code given by AAAA
overlay = new Bitmap(backDrop);
Graphics g = Graphics.FromImage(overlay);
重复循环此块现在可以实现稳定且低内存占用。此代码使用Visual Studio 2015社区。
答案 3 :(得分:1)
这仍然是一个数组
BitmapImage[] bimages = new BitmapImage[size];
数组是连续的固定长度数据结构,一旦为整个数组分配了内存,就无法回收它的一部分。尝试使用其他数据结构(如LinkedList<T>
)或其他更适合您的情况
答案 4 :(得分:1)
我只是告诉我有关回收BitmapImage内存的经验。我使用.Net Framework 4.5。我创建了简单的WPF应用程序并加载了一个大的图像文件。我尝试使用以下代码从内存中清除Image:
private void ButtonImageRemove_Click(object sender, RoutedEventArgs e)
{
image1.Source = null;
GC.Collect();
}
但它没有用。我也尝试了其他解决方案,但我没有得到答案。经过几天的挣扎,我发现如果按两次按钮,GC将释放内存。然后我只需编写此代码,在点击按钮后几秒钟调用GC收集器。
private void ButtonImageRemove_Click(object sender, RoutedEventArgs e)
{
image1.Source = null;
System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(delegate
{
System.Threading.Thread.Sleep(500);
GC.Collect();
}));
thread.Start();
}
此代码刚刚在DotNetFr 4.5中测试过。也许你必须为较低的.Net Framework冻结BitmapImage对象
的修改
除非布局得到更新,否则本规范不起作用。我的意思是如果父控制被删除,GC无法收回它。