如何显式地清除byte[]

本文关键字:byte 清除 何显式 | 更新日期: 2023-09-27 17:55:02

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

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

如何显式地清除byte[]

你怎么知道它们没有被收集?您提供的代码很好,如果没有任何悬空引用,它应该符合收集条件。

可以显式地通过

清除引用
outputMessage = null;

,你可以提示GC它应该这样做:

GC.Collect();

但是,不能保证您的对象将被收集,并且通常不需要手动干扰GC。

如果你一直在添加一个新的事件处理程序,你会删除任何旧的吗?如果没有,那么他们仍然为你保留参考。

编辑:为清晰起见,并通过OP

添加新信息

当托管数组离开作用域时,它被标记为垃圾收集。如果数组是值类型,则项的释放很快,但直到数组被收集才会发生。请记住,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内存:24,355,488字节(起始内存)
[1] GC内存:44,467,612字节(数组分配)
[2] GC内存:64,681,980 bytes(数组分配)
[3] GC内存:85,493,004字节(数组分配)
[4] GC内存:104,442,028字节(数组分配)
[5] GC内存:124,450,236字节(数组分配)
     GC内存:124,450,236字节(退出本地作用域)
     

所有作用域已退出:
GC内存:124,365,780字节(GC运行前)
     GC内存:24,356,996字节(GC收集运行后)

解决您的问题:

  1. 确保你没有挂在任何引用的byte[] s。在对数组的所有引用都被清除之前,字节不会被清除
  2. 在离开字节数组的作用域后显式调用GC.Collect()。一次它在范围之外,byte[]会自己清除,但你可以

确保没有对数组的引用。检查您没有对另一个变量赋值,该变量将数组保存在内存中。

您是否离开了outputMessage的焦点?
-如果在方法中声明:你是离开它,还是有一些(潜在的)无限循环并留在其中?
-如果在类对象中声明为全局:你的整个类是否通过引用this而保留在内存中?

在没有看到上下文的情况下很难确定。除非您保留对每个outputMessage的引用,否则每当GC决定运行时,它们最终都会被垃圾收集。

是什么让你看到私有字节一直在增长,永远不会缩小?另外,您是只在附带调试器的情况下运行还是在发布版中运行?

你真的需要每10秒创建一个新的数组吗?简单地使用Array可能会更快。清理并重用它。

考虑一下:是否有可能将byte[]封装在实现IDisposable的类中?使用之后,可以通过显式调用dispose()来处理对象。我不确定这是否能引导您找到解决方案,但这将是我的下一次尝试。

这个测试用例只在发布模式下工作。我正在使用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);
}