我如何明确清除字节[]

时间:2011-05-05 04:39:55

标签: c# .net memory-management memory-leaks garbage-collection

我正在创建新的字节数组,这些数组不是由GC收集的,而是存在于内存中并增加了专用字节数。下面的代码每10秒执行一次。完成后如何明确清除变量?

byte[] outputMessage = new byte[10000];
//Do some work here

6 个答案:

答案 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收集后)

解决您的问题:

  1. 确保您没有依赖byte[]的任何引用。 在对阵列的所有引用都是之前,字节不会被清除 走了,它完全超出了范围。
  2. 在离开字节数组的范围后显式调用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);
}