由于看似过早的“内存不足”异常,我们一直在仔细研究各种.NET构造的内存使用情况...特别是倾向于使大型对象堆碎片化的大型对象,从而导致“内存不足”异常。 .NET Image类:位图和图元文件,是一个令人惊讶的领域。
这是我们认为我们已经学到的内容,但是未能找到要验证的MS文档,因此,我们感谢其他人可以提供的任何确认:
(1)当您从压缩的光栅文件(JPG,PNG,GIF等)创建位图对象时,它将以该文件的完整分辨率消耗完全未压缩的像素阵列的内存。因此,例如,将9000x3000像素的5MB JPG扩展为9000x3000x3字节(假设24位彩色,没有alpha),或消耗的内存为81MB。正确的?
(1a)有证据(请参阅下面的2b)还存储了原始压缩格式...因此,在这种情况下实际上为86MB。但这还不清楚……有人知道吗?
(2)创建图元文件对象然后在其中绘制光栅文件(JPG,PNG,GIF等)时,它仅消耗压缩文件的内存。因此,如果将一个9000x3000像素的5MB JPG绘制到图元文件中,它将仅消耗大约5MB的内存。正确的?
(2a)要将栅格文件绘制到图元文件对象中,唯一的方法似乎是使用该文件加载位图,然后将位图绘制到图元文件中。有没有更好的方法而不涉及临时加载大量位图数据(并导致相关的内存碎片)?
(2b)当将位图绘制到图元文件中时,它使用的压缩格式的大小类似于原始压缩文件的大小。它是通过将原始压缩文件存储在位图中来实现的吗?还是通过使用原始压缩设置重新压缩扩展的位图来做到这一点?
(3)我们最初假定将大图像对象(> 85KB)放置在大对象堆中。实际上,事实并非如此。而是,每个位图和每个图元文件都是“小对象堆”中的24字节对象,该对象是指包含实际数据的 native 内存块。正确的?
(3a)我们假设这种 native 内存就像大对象堆一样,因为它无法压缩...一旦将大对象放入 native 内存中,它就永远不会被移动,因此 native 内存的碎片会引起与大对象堆的碎片。真的?还是对底层位图/图元文件数据进行了更有效的特殊处理?
(3b)因此,似乎有四个独立的内存块被分别管理,并且每个内存块用完都会导致相同的“内存不足”异常:小对象堆(受管理对象 85KB), native 内存(非托管对象,可能未压缩)和桌面堆(用于管理Windows句柄和此类有限资源)。我是否已正确记录了这四个文件?还有其他我们应该注意的吗?
任何人都可以提供的以上任何清晰度将不胜感激。如果有一本很好的书或文章可以充分说明上述内容,请告诉我。 (我很乐于完成必读的内容;但是绝大多数书籍都没有那么深入,因此不要告诉我任何我不知道的内容。)
谢谢!
最佳答案
有两种存储图像数据的方法:作为像素或向量。 Bitmap
关于像素,Metafile
关于像素和向量。矢量数据的存储效率更高。
为了允许操作位图,必须将其数据未压缩地存储在内存中。否则,GetPixel
和SetPixel
将必须解压缩,更改,重新压缩每个更改的位图(如果可能的话,甚至可以从头开始)。
Metafiles是由Microsoft创建的,旨在与GDI配合使用,因此它可能包含一些内存效率更高的压缩算法,这些算法可直接与图形卡一起使用。另外,图元文件没有GetPixel
SetPixel
方法,因此不必在内存中将其解压缩即可进行操作。
您不必关心运行时使用的内存池。还有更多方法,运行时可以决定将对象放置在何处。同样,您不应该担心使用(大)对象可能引起的内存不足异常。运行时将尽其所能(将对象放入其他对象之间的间隙,压缩堆,扩展可用的虚拟内存)以确保您不会遇到内存不足的异常。如果您确实以某种方式得到了此类异常,则可能是代码中应解决的另一个问题(例如内存泄漏)。
内存堆,映射和表的概述:(source)
同样,您认为超过85 KiB的对象放置在大对象堆上的假设也不是完全正确的。对于当前版本的CLR中的大多数对象,"is"是正确的,例如,在大对象堆上还分配了一个8 KiB的 double 数组(1000个 double 数组)。只需让运行时对此进行关注即可。
关于.net - .NET镜像类: Bitmap vs.图元文件的内存使用情况和碎片,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/15529859/