我正在创建新的字节数组,这些数组不是由GC收集的,而是存在于内存中并增加了专用字节数。下面的代码每10秒执行一次。完成后如何明确清除变量?
byte[] outputMessage = new byte[10000];
//Do some work here
答案 0 :(得分:9)
你怎么知道他们没有被收集?您提供的代码很好,如果您没有任何悬挂引用,它应该有资格收集。
明确地,您可以通过
清除引用outputMessage = null;
你可以提示GC它应该用它来完成它的工作:
GC.Collect();
但是,无法保证您的对象将被收集,并且通常不需要手动搞乱GC。
如果您要一直添加新的事件处理程序,是否要删除任何旧事件处理程序?如果没有,那么他们仍然会为您保留参考。
编辑:为了清晰起见,以及OP的新信息
答案 1 :(得分:2)
确保您没有对阵列的引用。检查您是否没有将该数组保留在内存中的另一个变量的赋值。
你是否留下了outputMessage的焦点?
- 如果它在一个方法中被声明:你是离开它还是有一些(无意的)无限循环并保留在其中?
- 如果它在类对象中被声明为全局:你的完整类是否通过引用保留在内存中?
答案 2 :(得分:2)
如果没有看到所使用的上下文,很难肯定地说。除非你保留对每个outputMessage的引用,否则每当GC决定运行时,它们最终会被垃圾收集。
你在看什么看到私有字节不断增长而且永远不会缩小?你也只是附带调试器运行,或者你是否在发布中运行?
你真的需要每10秒创建一个新阵列吗?简单地说Array.Clear并重用它可能会更快。
答案 3 :(得分:2)
考虑一下:是否可以将byte []封装在实现IDisposable的类中?使用后,您可以通过显式调用Dispose()来处置您的对象。我不确定这是否会引导您找到解决方案,但这将是我的下一次尝试。
答案 4 :(得分:2)
当托管数组离开作用域时,它会被标记为垃圾回收。如果数组是值类型,则项的释放很快但在收集数组之前不会发生 。请注意,byte
是值类型,但byte[]
是引用类型。
以下是一个快速示例:
void Main()
{
const int LOOPS = 5;
{
Console.WriteLine("When the arrays are kept inside the method's scope:");
Console.WriteLine(String.Format(" GC Memory: {0:N0} bytes (starting memory)", GC.GetTotalMemory(false)));
for(int i = 0; i < LOOPS; i++)
this.AllocateArray(i);
Console.WriteLine(String.Format(" GC Memory: {0:N0} bytes (exited local scope)", GC.GetTotalMemory(false)));
Console.WriteLine(String.Format(" GC Memory: {0:N0} bytes (after GC collection ran)", GC.GetTotalMemory(true)));
Console.WriteLine("\nWhen the arrays are outside the method's scope:");
Console.WriteLine(String.Format(" GC Memory: {0:N0} bytes (starting memory)", GC.GetTotalMemory(false)));
var arrays = new byte[LOOPS][];
for(int i = 0; i < LOOPS; i++)
this.AllocateArray(i, arrays);
Console.WriteLine(String.Format(" GC Memory: {0:N0} bytes (exited local scope)", GC.GetTotalMemory(false)));
Console.WriteLine(String.Format(" GC Memory: {0:N0} bytes (after GC collection ran)", GC.GetTotalMemory(true)));
arrays[0][0] = 1; // Prevent the arrays from being optimized away
}
Console.WriteLine("\nAll scopes exited:");
Console.WriteLine(String.Format(" GC Memory: {0:N0} bytes (before GC runs)", GC.GetTotalMemory(false)));
Console.WriteLine(String.Format(" GC Memory: {0:N0} bytes (after GC collection ran)", GC.GetTotalMemory(true)));
}
public void AllocateArray(int run)
{
var array = new byte[20000000];
Thread.Sleep(100); // Simulate work..
Console.WriteLine(String.Format("[{0}] GC Memory: {1:N0} bytes (local array allocated)", run+1, GC.GetTotalMemory(false)));
array[0] = 1; // Prevent the array from being optimized away
}
public void AllocateArray(int run, byte[][] arrays)
{
arrays[run] = new byte[20000000];
Thread.Sleep(100); // Simulate work..
Console.WriteLine(String.Format("[{0}] GC Memory: {1:N0} bytes (array allocated)", run+1, GC.GetTotalMemory(false)));
}
此输出看起来像这样(确切结果会有所不同):
当数组保持在方法范围内时:
GC内存:24,576,232字节(起始内存)
[1] GC内存:45,002,324字节(分配本地数组)
[2] GC内存:44,845,548字节(分配了本地数组)
[3] GC内存:64,574,296字节(分配了本地数组)
[4] GC内存:64,959,472字节(分配了本地数组)
[5] GC内存:44,675,340字节(分配了本地数组)
GC内存:44,675,340字节(退出本地范围)
GC内存:24,347,296字节(运行GC集合后)
当数组超出方法范围时:
GC内存:24,355,488字节(起始内存)
[1] GC内存:44,467,612字节(已分配数组)
[2] GC内存:64,681,980字节(已分配数组)
[3] GC内存:85,493,004字节(已分配数组)
[4] GC内存:104,442,028字节(已分配数组)
[5] GC内存:124,450,236字节(分配的数组)
GC内存:124,450,236字节(退出本地范围)
GC内存:124,357,588字节(运行GC采集后)
所有范围退出:
GC内存:124,365,780字节(GC运行前)
GC内存:24,356,996字节(运行GC收集后)
解决您的问题:
byte[]
的任何引用。
在对阵列的所有引用都是之前,字节不会被清除
走了,它完全超出了范围。GC.Collect()
。一旦
它超出范围,byte[]
将自行清除,但你可以
告诉它运行而不是等待。答案 5 :(得分:0)
此测试用例仅适用于发布模式。 我正在使用guid类创建一个数组(很简单)。它会有16个元素。
[TestMethod]
public void ByteArrayReleasesMemoryWhenTheyGoOutOfScope()
{
// *** WORKS ONLY IN RELEASE MODE ***
// arrange
var weakRef = new WeakReference(null);
// easy way to generate byte array
var byteArray = Guid.NewGuid().ToByteArray();
weakRef.Target = byteArray;
// act
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
// assert
Assert.IsFalse(weakRef.IsAlive);
}