- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
" + Thread.Current-6ren">
当通过 WriteableBitmap< 处理小文件(<= 32x32)时,我看到性能计数器“# Induced GC”(在完美的应用程序中应保持为零)快速增加
.
虽然这在小型应用程序中并不是一个重要的瓶颈,但当内存中存在数千个对象时,它就会成为一个非常大的问题(应用程序在 99.75%“% Time in GC”中每一步卡住几秒钟)(例如:EntityFramework
上下文加载了许多实体和关系)。
综合测试:
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);
我可以使用“const int Size = 48
”运行此测试十几次:它总是在约 1.5 秒内返回,并且“# Induced GC”有时会增加 1 或 2。
当我将“const int Size = 48
”更改为“const int Size = 32
”时,会发生非常非常糟糕的事情:“# Induced GC”增加每秒 10 个,现在总体运行时间超过一分钟:~80 秒![在具有 8GB RAM 的 Win7x64 Core-i7-2600//.NET 4.0.30319.237 上测试]
什么鬼!?
要么框架有一个非常严重的错误,要么我做的事情完全错误。
顺便说一句:
我不是通过进行图像处理来解决这个问题,而是通过 DataTemplate 使用包含针对某些数据库实体的图像的工具提示:当 RAM 中不存在太多对象时,这工作得很好(很快)——但是当存在数百万个其他对象(完全不相关)时,显示工具提示总是延迟几秒钟,而其他一切都工作正常。
最佳答案
TL;DR: 可能最好的解决方案是创建一个 WriteableBitmaps
的小型池。并重复使用它们,而不是创建它们然后扔掉它们。
因此,我开始使用 WinDbg 进行探索,看看是什么导致了收集的发生。
首先,我添加了对 Debugger.Break()
的调用到 Main
的开头让事情变得更容易。我还添加了自己的调用 GC.Collect()
作为健全性检查,以确保我的断点工作正常。然后在WinDbg中:
0:000> .loadby sos clr
0:000> !bpmd mscorlib.dll System.GC.Collect
Found 3 methods in module 000007feee811000...
MethodDesc = 000007feee896cb0
Setting breakpoint: bp 000007FEEF20E0C0 [System.GC.Collect(Int32)]
MethodDesc = 000007feee896cc0
Setting breakpoint: bp 000007FEEF20DDD0 [System.GC.Collect()]
MethodDesc = 000007feee896cd0
Setting breakpoint: bp 000007FEEEB74A80 [System.GC.Collect(Int32, System.GCCollectionMode)]
Adding pending breakpoints...
0:000> g
Breakpoint 1 hit
mscorlib_ni+0x9fddd0:
000007fe`ef20ddd0 4154 push r12
0:000> !clrstack
OS Thread Id: 0x49c (0)
Child SP IP Call Site
000000000014ed58 000007feef20ddd0 System.GC.Collect()
000000000014ed60 000007ff00140388 ConsoleApplication1.Program.Main(System.String[])
所以断点工作正常,但是当我让程序继续时,它再也没有被击中。 GC 例程似乎是从更深的地方调用的。接下来我走进GC.Collect()
函数来查看它正在调用什么。为了更轻松地做到这一点,我添加了第二个调用 GC.Collect()
紧接着第一个之后,就进入了第二个。这避免了单步执行所有 JIT 编译:
Breakpoint 1 hit
mscorlib_ni+0x9fddd0:
000007fe`ef20ddd0 4154 push r12
0:000> p
mscorlib_ni+0x9fddd2:
000007fe`ef20ddd2 4155 push r13
0:000> p
...
0:000> p
mscorlib_ni+0x9fde00:
000007fe`ef20de00 4c8b1d990b61ff mov r11,qword ptr [mscorlib_ni+0xe9a0 (000007fe`ee81e9a0)] ds:000007fe`ee81e9a0={clr!GCInterface::Collect (000007fe`eb976100)}
稍微走了一步后,我注意到对 clr!GCInterface::Collect
的引用这听起来很有希望。不幸的是,它的断点从未触发。进一步挖掘GC.Collect()
我发现clr!WKS::GCHeap::GarbageCollect
事实证明这是真正的方法。断点揭示了触发收集的代码:
0:009> bp clr!WKS::GCHeap::GarbageCollect
0:009> g
Breakpoint 4 hit
clr!WKS::GCHeap::GarbageCollect:
000007fe`eb919490 488bc4 mov rax,rsp
0:006> !clrstack
OS Thread Id: 0x954 (6)
Child SP IP Call Site
0000000000e4e708 000007feeb919490 [NDirectMethodFrameStandalone: 0000000000e4e708] System.GC._AddMemoryPressure(UInt64)
0000000000e4e6d0 000007feeeb9d4f7 System.GC.AddMemoryPressure(Int64)
0000000000e4e7a0 000007fee9259a4e System.Windows.Media.SafeMILHandle.UpdateEstimatedSize(Int64)
0000000000e4e7e0 000007fee9997b97 System.Windows.Media.Imaging.WriteableBitmap..ctor(Int32, Int32, Double, Double, System.Windows.Media.PixelFormat, System.Windows.Media.Imaging.BitmapPalette)
0000000000e4e8e0 000007ff00141f92 ConsoleApplication1.Program.<Main>b__c(Int32)
所以WriteableBitmap
的构造函数间接调用 GC.AddMemoryPressure ,最终导致集合(顺便说一句, GC.AddMemoryPressure
是模拟内存使用情况的更简单方法)。但这并不能解释尺寸从 33 变为 32 时行为的突然变化。
ILSpy在这里帮忙。特别是,如果您查看 SafeMILHandleMemoryPressure
的构造函数(由 SafeMILHandle.UpdateEstimatedSize
调用)你会看到它只使用 GC.AddMemoryPressure
如果要添加的压力 <= 8192。否则它使用自己的自定义系统来跟踪内存压力并触发集合。具有 32 位像素的 32x32 位图大小低于此限制,因为 WriteableBitmap
估计内存使用量为 32 * 32 * 4 * 2(我不确定为什么会有额外的因子 2)。
总之,您所看到的行为似乎是框架中启发式的结果,该框架不太适合您的情况。
您可以通过创建比您需要的尺寸更大或像素格式更大的位图来解决这个问题,以便位图的估计内存大小> 8192。
事后思考:我想这也表明集合是由于 GC.AddMemoryPressure
触发的。是否计入“# Induced GC”?
关于.net - 处理小图像(<=4k 像素数据)时强制进行 GC?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7331735/
我一直很难编辑我的 .htaccess 文件来一起做这三件事。我已经能够分别获得每个部分,但我只是不明白逻辑流程如何使它们全部工作。 这是我能够使用 bluehost support 上的演示进行整合
我制作的宏将模板工作簿保存为两个单独的文件。每个测试保存一个(位置 1、2、3 或 4),然后在另一个宏中使用每个测试的数据。第二个是保留用于备份的原始数据文件。现在的问题是每次我在每个位置运行测试并
我正在写一篇关于如何使用 OCaml 的模块系统而不是 Java 的 OO 系统(一个有趣的视角)的博客文章。我遇到了一些我不理解的关于强制的事情。下面是一个基本模块和两个包含它的模块: module
我有一段将被执行多次(5,000+)的代码,以及一个仅在第一次为真的 if 语句。我曾想过使用“FIRST”变量并每次都进行比较,但每次都检查它似乎是一种浪费,即使我知道它不需要。 bool FIRS
首先,我是 Perforce 的新手,我主要通过其文档进行学习。 因此,我们即将从 CVS 迁移到 Perforce,我最近学到了一个避免更改每个工作区的 P4CLIENT 的好方法,即在工作区根目录
我正在为一段代码编写测试,其中包含我试图涵盖的 IOException 捕获。 try/catch 看起来像这样: try { oos = new ObjectOutputStream(new
我正在尝试在新闻项目滚动之间添加延迟。我知道 $.each() 通过不等待动画完成来完成其工作,但我想知道如何制作它,以便一次向上滚动一个项目并等到最后一个动画完成后再继续在循环中。 $(functi
假设已经编写了一个方法,需要一个排序列表作为其输入之一。当然这将在代码中进行注释和记录,param 将被命名为“sortedList”,但如果有人忘记,则会出现错误。 有没有办法强制输入必须排序?我正
我正在尝试将传入请求重定向到 https://www.domain.com/和所有 https://www.domain.com/ {所有页面}并且没有什么麻烦。我试过的方法: 添加此行:Redire
我将如何实现以下内容: title_selection = raw_input("Please type in the number of your title and press Enter.\n%
我有一个登录表单,我需要强制关闭自动完成功能。我试过了 jquery: $('#login').attr("autocomplete", "off"); HTML: Javascript:docume
我想知道我应该怎么做才能强制从 dev 分支 merge 到我的 master 分支?使用“git merge dev”会导致很多冲突。但是,我不想单独处理它们。相反,我只是想使用我的 dev 分支中
当安装 Hl7.Fhir.DSTU2 和 Hl7.Fhir.R4 这两个 Nuget 包时,我们得到如下信息: DSTU2 包似乎在使用 Hl7.Fhir.Support.Poco 版本 3.4.0
我正在尝试让一个功能组件在 testFn 执行时强制重新渲染。我想使用状态来做到这一点(如果有更好的方法请说出来),这似乎成功地强制重新渲染但只有两次,然后什么都没有。 我构建了一个简单的演示来模拟这
默认情况下,g++ 似乎会省略未使用的类内定义方法的代码。示例 from my previous question : struct Foo { void bar() {} void baz(
我正在尝试使用 here 中介绍的技术使我的网站背景以比内容慢的速度滚动。我不希望背景固定,只希望更慢。 这是 HTML 的样子: .parallax { perspective: 1px;
我能找到的最相似的问题是 'how to create a row of scrollable text boxes or widgets in flutter inside a ListView?'
我有以下 eslint 配置: "object-curly-newline": ["error", { "ImportDeclaration": "never",
我正在使用 TinyMCE 插件并将 valid_elements 选项设置为: "a[href|target:_blank],strong/b,em/i,br,p,ul,ol,li" 即使没有列出数
您好,我想使用以下命令放置多行描述 p4 --field Description="MY CLN Header \\n my CLN complete description in two -thre
我是一名优秀的程序员,十分优秀!