强制释放巨大的内存阵列
Force deallocate huge memory array
在 C# 核心中,
我有一个 hugh 二进制数据数组,我需要在我的代码中使用它,我需要在使用完它时释放它分配的内存。
代码是 运行ning in docker for linux(使用基本图像:microsoft/dotnet:2.1-sdk + microsoft/dotnet:2.1- 运行time) - 我看到堆使用总是越来越大(通过 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();
}
}
}
谢谢。
答案的代码有效,但如果数组只有一个引用则无效:
即,如果我写:
byte[] b;
using (DisposeArr newArr = new DisposeArr(1024 * 1024 * 1024))
{
b = newArr.Data;
}
b = null;
GC.Collect();
上面的代码并没有真正释放元素,因为有对它的引用。
一切都必须直接引用newArr.data,所以代码中根本没有byte[]。
如果我们需要引用该对象,我发现的唯一方法是转换为其他 class,使用不安全创建。
public static class UnsafeConverter
{
// to do: convert only arrays of the same size short[] <-> ushort[]
private static unsafe T GetInstanceByPointer<T>(IntPtr pointer)
{
try
{
var fakeInstance = default(T);
var sizeInstance = default(int);
TypedReference typerefSize = __makeref(sizeInstance);
TypedReference typedReference = __makeref(fakeInstance);
*(IntPtr*)(&typedReference) = pointer;
T instance = __refvalue(typedReference, T);
return instance;
}
catch
{
return default;
}
}
public static unsafe T GetInstance<T>(object value)
{
try
{
GCHandle handle = GCHandle.Alloc(value);
IntPtr px = (IntPtr)handle;
T instance = GetInstanceByPointer<T>(px);
handle.Free();
return instance;
}
catch
{
return default;
}
}
}
并在 DisposeArr class:
public DisposeArr(byte[] arr)
{
Data = UnsafeConverter.GetInstance<byte[]>(arr);
}
如果我显式调用 Dispose,我需要在代码中将数组保存在某个集合中,并添加一个函数来检查该集合(只要没有任何处置)。
即:
private static ConcurrentDictionary<int, DisposeArr> _allData = new ConcurrentDictionary<int, DisposeArr>()
Class声明:
public class DisposeArr:IDisposable
{
...
public DisposeArr(long size)
{
Data = new byte[size];
_allData.TryAdd(GetHashCode(), this);
}
public static CollectAll()
{
for (var item in _allData)
{
_allData.Value.Dispose();
}
}
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
...
_allData.TryRemove(GetHashCode(), out DisposeArr tmpDisposeArr);
}
}
}
在 C# 核心中,
我有一个 hugh 二进制数据数组,我需要在我的代码中使用它,我需要在使用完它时释放它分配的内存。
代码是 运行ning in docker for linux(使用基本图像:microsoft/dotnet:2.1-sdk + microsoft/dotnet:2.1- 运行time) - 我看到堆使用总是越来越大(通过 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();
}
}
}
谢谢。
答案的代码有效,但如果数组只有一个引用则无效:
即,如果我写:
byte[] b;
using (DisposeArr newArr = new DisposeArr(1024 * 1024 * 1024))
{
b = newArr.Data;
}
b = null;
GC.Collect();
上面的代码并没有真正释放元素,因为有对它的引用。
一切都必须直接引用newArr.data,所以代码中根本没有byte[]。
如果我们需要引用该对象,我发现的唯一方法是转换为其他 class,使用不安全创建。
public static class UnsafeConverter
{
// to do: convert only arrays of the same size short[] <-> ushort[]
private static unsafe T GetInstanceByPointer<T>(IntPtr pointer)
{
try
{
var fakeInstance = default(T);
var sizeInstance = default(int);
TypedReference typerefSize = __makeref(sizeInstance);
TypedReference typedReference = __makeref(fakeInstance);
*(IntPtr*)(&typedReference) = pointer;
T instance = __refvalue(typedReference, T);
return instance;
}
catch
{
return default;
}
}
public static unsafe T GetInstance<T>(object value)
{
try
{
GCHandle handle = GCHandle.Alloc(value);
IntPtr px = (IntPtr)handle;
T instance = GetInstanceByPointer<T>(px);
handle.Free();
return instance;
}
catch
{
return default;
}
}
}
并在 DisposeArr class:
public DisposeArr(byte[] arr)
{
Data = UnsafeConverter.GetInstance<byte[]>(arr);
}
如果我显式调用 Dispose,我需要在代码中将数组保存在某个集合中,并添加一个函数来检查该集合(只要没有任何处置)。
即:
private static ConcurrentDictionary<int, DisposeArr> _allData = new ConcurrentDictionary<int, DisposeArr>()
Class声明:
public class DisposeArr:IDisposable { ...
public DisposeArr(long size)
{
Data = new byte[size];
_allData.TryAdd(GetHashCode(), this);
}
public static CollectAll()
{
for (var item in _allData)
{
_allData.Value.Dispose();
}
}
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
...
_allData.TryRemove(GetHashCode(), out DisposeArr tmpDisposeArr);
}
}
}