我制作了一个64位WPF测试应用程序.随着我的应用程序运行并打开任务管理器,我会查看系统内存使用情况.我看到我使用2GB,我有6GB可用.
在我的应用程序中,我单击"添加"按钮将新的1GB字节数组添加到列表中.我看到我的系统内存使用量增加了1GB.我点击添加总共6次,填充我开始时可用的6GB内存.
我单击"删除"按钮6次,从列表中删除每个数组.删除的字节数组不应该由我的控件中的任何其他对象引用.
当我删除时,我没有看到我的记忆下降.但这对我来说没关系,因为我知道GC是非确定性的,所有这一切.我认为GC会根据需要收集.
所以现在内存看起来很满,但是期望GC在需要时收集,我再次添加.我的电脑开始滑入和跳出磁盘晃动昏迷.为什么GC不收集?如果不是时候做,那么什么时候?
作为一个完整性检查,我有一个强制GC的按钮.当我推动它时,我很快就恢复了6GB.这不能证明我的6个阵列没有被引用,并且如果GC知道/想要收集COULD吗?
我已经阅读了很多说我不应该调用GC.Collect()但是如果GC在这种情况下不收集,我还能做什么?
private ObservableCollection<byte[]> memoryChunks = new ObservableCollection<byte[]>();
public ObservableCollection<byte[]> MemoryChunks
{
get { return this.memoryChunks; }
}
private void AddButton_Click(object sender, RoutedEventArgs e)
{
// Create a 1 gig chunk of memory and add it to the collection.
// It should not be garbage collected as long as it's in the collection.
try
{
byte[] chunk = new byte[1024*1024*1024];
// Looks like I need to populate memory otherwise it doesn't show up in task …Run Code Online (Sandbox Code Playgroud) 我们的应用程序不断为大量数据(比如几十到几百兆字节)分配数组,这些数据在丢弃之前会存在很短的时间.
天真地做这会导致大对象堆碎片,最终导致应用程序崩溃与OutOfMemoryException,尽管当前活动对象的大小不会过多.
我们过去成功管理过这种方法的一种方法是对阵列进行分块以确保它们不会最终出现在LOH上,其目的是通过允许内存被垃圾收集器压缩来避免碎片化.
我们的最新应用程序处理的数据比以前更多,并且在单独的AppDomain或单独进程中托管的加载项之间非常频繁地传递此序列化数据.我们采用了与以前相同的方法,确保我们的内存总是被分块并且非常小心地避免大对象堆分配.
但是我们有一个必须在外部32位进程中托管的加载项(因为我们的主应用程序是64位,加载项必须使用32位库).在特别繁重的负载下,当很多SOH内存块被快速分配并在之后不久丢弃时,即使我们的分块方法还不足以保存我们的32位加载项,并且它会因OutOfMemoryException而崩溃.
在发生OutOfMemoryException时使用WinDbg,!heapstat -inclUnrooted显示如下:
Heap Gen0 Gen1 Gen2 LOH
Heap0 24612 4166452 228499692 9757136
Free space: Percentage
Heap0 12 12 4636044 12848SOH: 1% LOH: 0%
Unrooted objects: Percentage
Heap0 72 0 5488 0SOH: 0% LOH: 0%
Run Code Online (Sandbox Code Playgroud)
!dumpheap -stat 显示这个:
-- SNIP --
79b56c28 3085 435356 System.Object[]
79b8ebd4 1 1048592 System.UInt16[]
79b9f9ac 26880 1301812 System.String
002f7a60 34 4648916 Free
79ba4944 6128 87366192 System.Byte[]
79b8ef28 17195 145981324 System.Double[]
Total 97166 objects
Fragmented blocks larger than 0.5 MB: …Run Code Online (Sandbox Code Playgroud) 我正在处理类似这样的代码(从这里开始)
using (var bmp = new System.Drawing.Bitmap(1000, 1000))
{
IntPtr hBitmap = bmp.GetHbitmap();
var source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
hBitmap, IntPtr.Zero, Int32Rect.Empty,
System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
}
Run Code Online (Sandbox Code Playgroud)
和MSDN说,每当我打电话Bitmap.GetHbitmap()我要叫DeleteObject()上hBitmap释放非托管资源。
我看到的所有答案和 MSDN 都说我必须 P/InvokeDeleteObject()函数。这看起来有点疯狂,因为我获得了hBitmap没有 P/Invoke 的,现在我突然需要使用 P/Invoke 来正确摆脱它。
除了 P/Invoke 之外确实没有其他方法可以DeleteObject()调用 myhBitmap吗?