当通过WriteableBitmap
处理小文件(
尽管这不是小型应用程序中的主要瓶颈,但是当内存中存在数千个对象时,它成为一个非常大的问题(应用程序在99.75%的“%Time in GC时间”中冻结几秒钟)(例如: cc>包含许多实体和关系的上下文)。
综合测试:
var objectCountPressure = (
from x in Enumerable.Range(65, 26)
let root = new DirectoryInfo((char)x + ":\\")
let subs =
from y in Enumerable.Range(0, 100 * IntPtr.Size)
let sub =new {DI = new DirectoryInfo(Path.Combine(root.FullName, "sub" + y)), Parent = root}
let files = from z in Enumerable.Range(0, 400) select new {FI = new FileInfo(Path.Combine(sub.DI.FullName, "file" + z)), Parent = sub}
select new {sub, files = files.ToList()}
select new {root, subs = subs.ToList()}
).ToList();
const int Size = 32;
Action<int> handler = threadnr => {
Console.WriteLine(threadnr + " => " + Thread.CurrentThread.ManagedThreadId);
for (int i = 0; i < 10000; i++) {
var wb = new WriteableBitmap(Size, Size, 96, 96, PixelFormats.Bgra32, null);
wb.Lock();
var stride = wb.BackBufferStride;
var blocks = stride / sizeof(int);
unsafe {
var row = (byte*)wb.BackBuffer;
for (int y = 0; y < wb.PixelHeight; y++, row += stride)
{
var start = (int*)row;
for (int x = 0; x < blocks; x++, start++)
*start = i;
}
}
wb.Unlock();
wb.Freeze(); }
};
var sw = Stopwatch.StartNew();
Console.WriteLine("start: {0:n3} ms", sw.Elapsed.TotalMilliseconds);
Parallel.For(0, Environment.ProcessorCount, new ParallelOptions{MaxDegreeOfParallelism = Environment.ProcessorCount}, handler);
Console.WriteLine("stop : {0:n2} s", sw.Elapsed.TotalSeconds);
GC.KeepAlive(objectCountPressure);
我可以使用“
EntityFramework
”多次运行此测试:它总是在〜1.5s内返回,并且“#Induced GC”有时会增加1或2。当我将“
const int Size = 48
”更改为“ const int Size = 48
”时,发生了非常非常糟糕的事情:“#诱导GC”每秒增加10次,现在整个运行时间超过一分钟:〜80秒![在Win7x64 Core-i7-2600上测试,带有8GB RAM // .NET 4.0.30319.237]
WTF !?
框架有一个非常严重的错误,或者我做的是完全错误的事情。
顺便说一句:
我不是通过图像处理来解决这个问题的,而只是通过使用包含通过DataTemplate对某些数据库实体的Image的工具提示来解决这个问题:
当RAM中没有太多对象时,此方法运行良好(快速),但是当存在数百万个其他对象(完全不相关)时,则显示工具提示始终会延迟几秒钟,而其他所有情况都很好。
最佳答案
在所有SafeMILHandleMemoryPressure
和SafeMILHandle
废话之下,是对MS.Internal.MemoryPressure
上方法的调用,该方法使用静态字段“ _totalMemory
”来跟踪WPF认为分配了多少内存。当达到(相当小的)限制时,诱导的GC将开始并且永远不会结束。
您可以使用一点反射魔术就完全停止WPF的这种行为。只需将_totalMemory
设置为适当的负数,这样就永远不会达到限制,并且永远不会发生诱导的GC:
typeof(BitmapImage).Assembly.GetType("MS.Internal.MemoryPressure")
.GetField("_totalMemory", BindingFlags.NonPublic | BindingFlags.Static)
.SetValue(null, Int64.MinValue / 2);
关于.net - 处理小图像(<= 4k像素数据)时是否强制使用GC?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/7331735/