在 CF.NET 中创建大位图时出现 OutOfMemoryException
我的紧凑框架应用程序通过将所有项目渲染到大位图表面,然后将该位图复制到屏幕上的偏移位置,以便仅显示适当的项目,来创建平滑滚动列表。 旧版本仅渲染当时应该出现在屏幕上的项目,但这种方法对于平滑滚动界面来说太慢了。
最初创建大位图时,它偶尔会生成 OutOfMemoryException。 如果用户对设备执行软重置并再次运行应用程序,则可以毫无问题地执行创建。
看起来这个位图不是在程序内存中生成的,因为应用程序使用的程序内存量与新的平滑滚动方法之前大致相同。
有什么方法可以防止这种异常吗? 有什么方法可以在抛出异常之前释放我需要的内存(无论它在哪里)?
My compact framework application creates a smooth-scrolling list by rendering all the items to a large bitmap surface, then copying that bitmap to an offset position on the screen so that only the appropriate items show. Older versions only rendered the items that should appear on screen at the time, but this approach was too slow for a smooth scrolling interface.
It occasionally generates an OutOfMemoryException when initially creating the large bitmap. If the user performs a soft-reset of the device and runs the application again, it is able to perform the creation without issue.
It doesn't look like this bitmap is being generated in program memory, since the application uses approximately the same amount of program memory as it did before the new smooth-scrolling methods.
Is there some way I can prevent this exception? Is there any way I can free up the memory I need (wherever it is) before the exception is thrown?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我建议回到仅渲染部分数据的旧机制,因为完全渲染数据的大小显然是一个问题。 为了帮助防止渲染问题,我可能会预先渲染当前视图上方和下方的几行,以便它们可以在有限的影响下“滚动”。
I'd suggest going back to the old mechanism of rendering only part of the data, as the size of the fully-rendered data is obviously an issue. To help prevent rendering problems I would probably pre-render a few rows above and below the current view so they can be "scrolled" in with limited impact.
我一发布,就想到了您可以采取一些措施来解决新版本的问题。 您遇到的问题之一是 CF 试图找到一块可用于巨大位图的连续内存,这有时是一个问题。
您可以创建一组较小的位图,每个项目一个,然后将每个项目渲染到其自己的小位图上,而不是创建一个大位图。 在显示过程中,您只需复制所需的位图即可。 CF 创建一堆小位图比创建一个大位图要容易得多,并且您不应该遇到任何内存问题,除非这是一堆真正巨大的项目。
我应该避免使用“没有解决办法”之类的表达方式。
另一重要点:确保在完成每个位图后调用 Dispose()。
And just as soon as I posted I thought of something you can do to fix your problem with the new version. The problem you have is one of CF trying to find one block of contiguous memory available for the huge bitmap, and this is occasionally a problem.
Instead of creating one big bitmap, you can instead create a collection of smaller bitmaps, one for each item, and render each item onto its own little bitmap. During display, you then just copy over the bitmaps you need. CF will have a much easier time creating a bunch of little bitmaps than one big one, and you shouldn't have any memory problems unless this is a truly enormous bunch of items.
I should avoid expressions like "there is no fix".
One other important point: make sure you call Dispose() on each bitmap when you're finished with it.
您的位图肯定是在程序内存中创建的。 位图需要多少内存取决于它有多大,而所需的大小是否会生成 OutOfMemoryException 取决于 PDA 可用的内存量(这使得这是一个随机发生的错误)。
抱歉,但这通常是一种不可取的控制渲染技术(尤其是在 Compact Framework 上),除了增加 PDA 上的物理内存之外没有其他解决方案,而这通常是不可能的(并且通常无法解决问题) ,因为无论设备有多少可用空间,CF 进程都限制为 32MB)。
最好的选择是回到旧版本并提高其渲染速度。 CF 上还有一种简单的技术可以使控件双缓冲以消除闪烁。
Your bitmap definitely is being created in program memory. How much memory the bitmap needs depends on how big it is, and whether or not this required size will generate the OutOfMemoryException depends on how much is available to the PDA (which makes this a randomly-occuring error).
Sorry, but this is generally an inadvisable control rendering technique (especially on the Compact Framework) for which there is no fix short of increasing the physical memory on the PDA, which isn't usually possible (and often won't fix the problem anyway, since a CF process is limited to 32MB no matter how much the device has available).
Your best bet is to go back to the old version and improve its rendering speed. There is also a simple technique available on CF for making a control double-buffered to eliminate flicker.
由于您似乎遇到了设备限制,限制了您可以创建的位图空间的总大小(这些显然是在视频 RAM 而不是通用程序内存中创建的),一种替代方法是将此处使用的大位图对象替换为一个普通的 Windows 内存块,通过 PInvoking BitBlt API 函数来访问它以进行读写。
最初创建内存块很棘手,您可能想问另一个有关此问题的问题(GCHandle.Alloc 可以在此处用于创建“固定”对象,这意味着 .NET 不允许在记忆,这在这里至关重要)。 我知道该怎么做,但我不确定我做得是否正确,我宁愿听取专家的意见。
创建大块后,您将迭代您的项目,将每个项目渲染为您不断重复使用的一个小位图(使用现有的 .NET 代码),并将其 BitBlt 到内存块中的适当位置。
创建整个缓存后,您的渲染代码应该像以前一样工作,不同之处在于您不是从大位图复制到渲染表面,而是从缓存块进行 BitBlt。 BitBlt 的参数本质上与 DrawImage 相同(目标、源、坐标和大小等)。
由于您以这种方式使用常规内存而不是专用视频 RAM 创建缓存,因此我认为您不会遇到同样的问题。 但是,我肯定会让块创建代码首先工作并进行测试,以确保它每次都能创建足够大的块。
更新:实际上,理想的方法是拥有一组较小的内存块,而不是一个大的内存块(就像我认为是位图方法的问题一样),但您已经有足够的事情要做。 我曾经使用过处理 5MB 和 10MB 对象的 CF 应用程序,无论如何这都不是一个大问题(尽管当该块被固定时它可能是一个更大的问题 - 我不知道)。 顺便说一句,我一直对位图创建时的 OOME 感到惊讶,因为我知道位图比可用内存小得多,你也是如此 - 现在我知道为什么了。 抱歉,我一开始以为这是一个简单的解决方案。
Since it appears you've run into a device limitation that is restricting the total size of Bitmap space you can create (these are apparently created in video RAM rather than general program memory), one alternative is to replace the big Bitmap object used here with a plain-old block of Windows memory, accessing it for reading and writing by PInvoking the BitBlt API function.
Initially creating the memory block is tricky, and you'd probably want to ask another SO question about that (GCHandle.Alloc can be used here to create a "pinned" object, which means .NET isn't allowed to move it around in memory, which is critical here). I know how to do it, but I'm not sure I do it correctly and I'd rather have an expert's input.
Once you've created the big block, you'd iterate through your items, render each to one small bitmap that you keep re-using (using your existing .NET code), and BitBlt it to the appropriate spot in your memory block.
After creating the entire cache, your rendering code should work just like before, with the difference that instead of copying from the big bitmap to your rendering surface, you BitBlt from your cache block. The arguments for BitBlt are essentially the same as for DrawImage (destination, source, coordinates and sizes etc.).
Since you're creating the cache out of regular memory this way instead of specialized video RAM, I don't think you'll run into the same problem. However, I would definitely get the block creation code working first and test to make sure it can create a big enough block every time.
Update: actually, the ideal approach would be to have a collection of smaller memory blocks rather than one big one (like I thought was the problem with the Bitmap approach), but you already have enough to do. I've worked with CF apps that deal with 5 and 10MB objects and it's not a huge problem anyway (although it might be a bigger problem when that chunk is pinned - I dunno). BTW, I've always been surprised by the OOMEs on BitMap creation because I knew the bitmaps were much smaller than the available memory, as did you - now I know why. Sorry I thought this was an easy solve at first.