【发布时间】:2019-01-23 08:29:41
【问题描述】:
在 C# 核心中,
我有一个庞大的二进制数据数组,需要在我的代码中使用,我需要在完成使用后释放它分配的内存。
代码在 docker for linux 中运行(使用基础镜像:microsoft/dotnet:2.1-sdk + microsoft/dotnet:2.1-runtime) - 我看到堆使用量总是越来越大(通过 htop)。
byte[] arr = new byte[1024*1024*1024];
Console.WriteLine("array in gen:{0}", GC.GetGeneration(arr));
// 数组在第 2 代。
当我这样做时:
Console.WriteLine("total mem before:{0}", GC.GetTotalMemory(false));
GC.Collect();
Console.WriteLine("total mem after:{0}", GC.GetTotalMemory(false));
数组分配的内存没有被释放,collect似乎没有立即生效?
我也尝试过将 arr 放入 IDisposable,但它有帮助吗?
public class DisposeArr : IDisposable
{
// Flag: Has Dispose already been called?
bool disposed = false;
// Instantiate a SafeHandle instance.
SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);
byte[] Data { get; set; }
public DisposeArr(long size)
{
Data = new byte[size];
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
Data = null;
handle.Dispose();
// Free any other managed objects here.
//
}
disposed = true;
}
~DisposeArr()
{
Dispose(false);
}
}
在主代码中:
using (DisposeArr newArr = new DisposeArr(1024 * 1024 * 1024))
{
}
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("total mem before:{0}", GC.GetTotalMemory(false));
GC.Collect();
Console.WriteLine("total mem after:{0}", GC.GetTotalMemory(false));
如何强制数组分配的内存在 'GC.Collect' 之后立即释放?
这是完整的代码。 该代码可能有效,但如果有 100% 和 95%(几乎是内存),则可能存在一些内存泄漏(在 docker 上 - 在 linux 中 - 这可能会遇到内存泄漏)。
using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;
namespace TestHeap
{
public class DisposeArr : IDisposable
{
// Flag: Has Dispose already been called?
bool disposed = false;
// Instantiate a SafeHandle instance.
SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);
byte[] Data { get; set; }
public DisposeArr(long size)
{
Data = new byte[size];
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
Data = null;
handle.Dispose();
// Free any other managed objects here.
//
}
disposed = true;
}
~DisposeArr()
{
Dispose(false);
}
}
class Program
{
public void Run()
{
for (int i = 0; i < 100; i++)
{
Console.WriteLine("total mem:{0}", GC.GetTotalMemory(false)); GC.Collect();
using (DisposeArr newArr = new DisposeArr(1024 * 1024 * 1024))
{
}
// byte[] arr = new byte[1024 * 1024 * 1024];
Console.WriteLine("New. total mem:{0}", GC.GetTotalMemory(false));
GC.Collect();
Console.WriteLine("Collect. total mem:{0}", GC.GetTotalMemory(false));
GC.WaitForPendingFinalizers();
Console.WriteLine("Pending. total mem:{0}", GC.GetTotalMemory(false)); GC.Collect();
GC.Collect();
Console.WriteLine("Collect. total mem:{0}", GC.GetTotalMemory(false));
}
Console.ReadLine();
}
static void Main(string[] args)
{
Program p = new Program();
p.Run();
}
}
}
谢谢。
【问题讨论】:
-
几个 cmets:1) 由于其大小,该数组最终位于大对象堆上。 2) Dispose 与 C# 中的释放内存无关。 3)看起来您正在从不同的代码片段提供 sn-ps,例如在您的第一个示例中,该数组称为
arr,但您会生成b指向的内容。你能提供一个例子来说明这个数组没有被GCed吗? -
另外,您是处于调试模式还是发布模式?调试模式将使引用保持更长时间。
-
你为什么觉得有必要这样做?
-
我修复了代码(arr vs b)。不过,我不知道如何释放分配的内存。另外 - 我在 linux docker 中运行代码(可能代码运行方式与 GC 不同) - microsoft/dotnet:2.1-sdk + icrosoft/dotnet:2.1-runtime
-
请提供一个显示问题的最小、完整示例。此外,请确保您使用的是发布模式构建。
标签: c# .net-core garbage-collection idisposable