mmr*_*mmr 40 .net c# garbage-collection memory-management
我有一个用于图像处理的应用程序,我发现自己通常会分配4000x4000 ushort大小的数组,以及偶尔浮点数等.目前,.NET框架在这个应用程序中往往会随机崩溃,几乎总是出现内存不足错误.32mb不是一个巨大的声明,但如果.NET碎片化内存,那么这种大型连续分配很可能不会像预期的那样运行.
有没有办法告诉垃圾收集器更积极,或碎片整理内存(如果这是问题)?我意识到有GC.Collect和GC.WaitForPendingFinalizers调用,我通过我的代码非常自由地散布它们,但我仍然得到错误.这可能是因为我正在调用使用本机代码的dll例程,但我不确定.我已经查看了那个C++代码,并确保我声明删除的任何内存,但我仍然得到这些C#崩溃,所以我很确定它不存在.我想知道C++调用是否会干扰GC,使其留下内存,因为它曾经与本机调用交互 - 这可能吗?如果是这样,我可以关闭该功能吗?
编辑:这是一些非常具体的代码,将导致崩溃.根据这个问题,我不需要在这里处理BitmapSource对象.这是天真的版本,没有GC.Collects.它通常在撤销过程的迭代4到10上崩溃.这段代码替换了空白WPF项目中的构造函数,因为我使用的是WPF.由于我在下面对@dthorpe的回答中解释的限制以及此SO问题中列出的要求,我对bitmapsource做了一些古怪的事情.
public partial class Window1 : Window {
public Window1() {
InitializeComponent();
//Attempts to create an OOM crash
//to do so, mimic minute croppings of an 'image' (ushort array), and then undoing the crops
int theRows = 4000, currRows;
int theColumns = 4000, currCols;
int theMaxChange = 30;
int i;
List<ushort[]> theList = new List<ushort[]>();//the list of images in the undo/redo stack
byte[] displayBuffer = null;//the buffer used as a bitmap source
BitmapSource theSource = null;
for (i = 0; i < theMaxChange; i++) {
currRows = theRows - i;
currCols = theColumns - i;
theList.Add(new ushort[(theRows - i) * (theColumns - i)]);
displayBuffer = new byte[theList[i].Length];
theSource = BitmapSource.Create(currCols, currRows,
96, 96, PixelFormats.Gray8, null, displayBuffer,
(currCols * PixelFormats.Gray8.BitsPerPixel + 7) / 8);
System.Console.WriteLine("Got to change " + i.ToString());
System.Threading.Thread.Sleep(100);
}
//should get here. If not, then theMaxChange is too large.
//Now, go back up the undo stack.
for (i = theMaxChange - 1; i >= 0; i--) {
displayBuffer = new byte[theList[i].Length];
theSource = BitmapSource.Create((theColumns - i), (theRows - i),
96, 96, PixelFormats.Gray8, null, displayBuffer,
((theColumns - i) * PixelFormats.Gray8.BitsPerPixel + 7) / 8);
System.Console.WriteLine("Got to undo change " + i.ToString());
System.Threading.Thread.Sleep(100);
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在,如果我明确地调用垃圾收集器,我必须将整个代码包装在外部循环中以导致OOM崩溃.对我来说,这往往发生在x = 50左右:
public partial class Window1 : Window {
public Window1() {
InitializeComponent();
//Attempts to create an OOM crash
//to do so, mimic minute croppings of an 'image' (ushort array), and then undoing the crops
for (int x = 0; x < 1000; x++){
int theRows = 4000, currRows;
int theColumns = 4000, currCols;
int theMaxChange = 30;
int i;
List<ushort[]> theList = new List<ushort[]>();//the list of images in the undo/redo stack
byte[] displayBuffer = null;//the buffer used as a bitmap source
BitmapSource theSource = null;
for (i = 0; i < theMaxChange; i++) {
currRows = theRows - i;
currCols = theColumns - i;
theList.Add(new ushort[(theRows - i) * (theColumns - i)]);
displayBuffer = new byte[theList[i].Length];
theSource = BitmapSource.Create(currCols, currRows,
96, 96, PixelFormats.Gray8, null, displayBuffer,
(currCols * PixelFormats.Gray8.BitsPerPixel + 7) / 8);
}
//should get here. If not, then theMaxChange is too large.
//Now, go back up the undo stack.
for (i = theMaxChange - 1; i >= 0; i--) {
displayBuffer = new byte[theList[i].Length];
theSource = BitmapSource.Create((theColumns - i), (theRows - i),
96, 96, PixelFormats.Gray8, null, displayBuffer,
((theColumns - i) * PixelFormats.Gray8.BitsPerPixel + 7) / 8);
GC.WaitForPendingFinalizers();//force gc to collect, because we're in scenario 2, lots of large random changes
GC.Collect();
}
System.Console.WriteLine("Got to changelist " + x.ToString());
System.Threading.Thread.Sleep(100);
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果我在任何一种情况下都错误处理了内存,如果有什么东西我应该发现一个分析器,请告诉我.那是一个非常简单的例程.
不幸的是,看起来@ Kevin的答案是正确的 - 这是.NET中的一个错误以及.NET如何处理大于85k的对象.这种情况让我非常奇怪; 可以使用这种限制或任何其他Office套件应用程序在.NET中重写Powerpoint吗?85k在我看来并不是一个很大的空间,而且我还认为任何使用所谓"大"分配的程序在使用.NET时会在几天到几周内变得不稳定.
编辑:看起来凯文是对的,这是.NET的GC的限制.对于那些不想遵循整个线程的人,.NET有四个GC堆:gen0,gen1,gen2和LOH(大对象堆).根据创建时间(从gen0移动到gen1到gen2等),所有85k或更小的东西都在前三个堆中的一个上.大于85k的物体放置在LOH上.LOH 永远不会被压缩,所以最终,我正在进行的类型的分配最终会导致OOM错误,因为对象会分散在该内存空间中.我们发现迁移到.NET 4.0确实有点帮助了这个问题,延迟了异常,但没有阻止它.老实说,在.NET中讨论GC).对于记录,Java不会在其GC中出现此行为.
kem*_*002 22
以下是一些详细介绍大对象堆问题的文章.这听起来像你可能遇到的.
大型对象堆的危险:http:
//www.simple-talk.com/dotnet/.net-framework/the-dangers-of-the-large-object-heap/
以下是有关如何收集大对象堆(LOH)数据的链接:http:
//msdn.microsoft.com/en-us/magazine/cc534993.aspx
据此,似乎没有办法压缩LOH.我找不到任何明确说明如何做的更新的东西,所以它似乎在2.0运行时没有改变:http:
//blogs.msdn.com/maoni/archive/2006/04/18/大对象heap.aspx
处理问题的简单方法是尽可能制作小对象.您的另一个选择是只创建几个大对象并反复重复使用它们.不是一个想法的情况,但它可能比重写对象结构更好.由于您确实说过创建的对象(数组)大小不同,因此可能很难,但它可能会使应用程序崩溃.
Pao*_*olo 22
首先缩小问题所在.如果您有本机内存泄漏,那么戳GC不会为您做任何事情.
运行perfmon并查看.NET堆大小和专用字节计数器.如果堆大小保持相当稳定但私有字节增长,那么你就会遇到本机代码问题,你需要打破C++工具来调试它.
假设问题出在.NET堆上,你应该对像Redgate的Ant分析器或JetBrain的DotTrace这样的代码运行一个分析器.这将告诉您哪些对象占用了空间而没有快速收集.您也可以将WinDbg与SOS一起使用,但它是一个繁琐的界面(虽然功能强大).
一旦找到了违规物品,应该更明白如何处理它们.导致问题的一些事情是引用对象的静态字段,未注册的事件处理程序,生存时间足以进入Gen2但随后很快就会死亡的对象等等.如果没有内存堆的配置文件,您将不会能够找到答案.
无论你做什么,"自由地洒"GC.Collect调用几乎总是尝试解决问题的错误方法.
切换到GC的服务器版本有可能改善事物(只是配置文件中的属性) - 默认工作站版本旨在保持UI响应,因此将有效地放弃大量长时间运行的选择.