有人可以解释垃圾收集器的行为吗? [英] Could someone explain the behaviour of the garbage collector?

查看:143
本文介绍了有人可以解释垃圾收集器的行为吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我在C#中使用垃圾收集器(或者更确切地说是CLR),试图更好地理解C#中的内存管理。将三个较大的文件读入一个 byte [] 缓冲区。我希望看到,如果我真的需要任何东西来处理内存有效性的话,那么我们就可以看到


  • >在当前迭代结束后将 byte [] 设置为null

  • 时,它会产生任何影响,最后如果它有帮助当通过 GC.Collect()强制垃圾回收时,使用Windows任务管理器测量内存消耗并四舍五入。我尝试了几次,但总体来说它仍然保持不变。



    以下是我的简单示例程序:

     static void Main(string [] args)
    {
    Loop();
    }

    private static void Loop()
    {
    var list = new List< string>
    {
    @C:\ Users \Public\Music\Sample Music\Amanda.wma,//大小:4.75 MB
    @C:\ Users \ Public \ Music \Sample Music \Despertar.wma,//大小:5.92 MB
    @C:\ Users \Public\Music\Sample Music\Distance.wma ,//尺寸:6.31 MB
    };

    Console.WriteLine(before loop);
    Console.ReadLine();

    foreach(列表中的字符串路径名)
    {
    // ...此处的代码...

    Console.WriteLine(in loop );
    Console.ReadLine();
    }

    Console.WriteLine(GC.CollectionCount(1));
    Console.WriteLine(end loop);
    Console.ReadLine();
    }

    对于每个测试,我只更改 foreach 循环。然后我运行该程序,在每个 Console.ReadLine()停止并在Windows任务管理器中检查进程的内存使用情况。我记下了用过的内存,然后继续程序返回(我知道断点;))。在循环结束之后,我将 GC.CollectionCount(1)写入控制台,以查看GC跳入的频率。






    结果





    测试1:

      foreach(...)
    {
    byte [] buffer = File.ReadAllBytes(pathname);

    Console.WriteLine ...



    $ p $结果(内存使用):

     循环前:9.000 K 
    1.迭代:13.000 K
    2.迭代:19.000 K
    3.迭代:25.000 K
    循环后:25.000 K
    GC.CollectionCount(1):2


    <$ p <$ p




    $ p $ foreach(...)
    {
    byte [] buffer = File.ReadAllBytes(pathname);
    buffer = null;

    Console.WriteLine ...



    $ p $结果(内存使用):

     循环前:9.000 K 
    1.迭代:13.000 K
    2.迭代:14.000 K
    3.迭代:15.000 K
    循环后:15.000 K
    GC.CollectionCount(1):2






    测试3:

     foreach(...)
    {
    byte [] buffer = File.ReadAllBytes(pathname);
    buffer = null;
    GC.Collect();

    Console.WriteLine ...



    $ p $结果(内存使用):

     循环前:9.000 K 
    1.迭代:8.500 K
    2.迭代:8.600 K
    3.迭代:8.600 K
    循环后:8.600 K
    GC.CollectionCount(1):3









    我不明白:




    • 在测试1中,内存随着每次迭代而增加。因此我猜测内存不会在循环结束时被释放。但GC仍然说它收集了2次( GC.CollectionCount )。怎么样?

    • 在测试2中,显然帮助将 buffer 设置为 null 。内存低于测试2中。但为什么 GC.CollectionCount 输出2而不是3? 为什么内存使用量不如测试3中那么低?

    • 测试3使用最少的内存。我会说这是因为1.移除了对内存的引用( buffer 设置为 null )并且因此,当通过 GC.Collect()调用垃圾回收器时,它可以释放内存。似乎很清楚。
    • 如果有更多经验的人可以对以上几点有所了解,那对我来说真的很有帮助。相当有趣的主题imho。

      解决方案

      看着你正在阅读整个WMA文件到一个数组中,我会说那些数组对象正在大对象堆中分配。这是一个单独的堆,它以更多的malloc类型的方式进行管理(因为压缩垃圾回收在处理大对象方面效率不高)。



      大对象中的空间堆是根据不同的规则收集的,它不会计入主生成计数,并且即使内存正在被重用,测试1和2之间的集合数量也没有差异(所收集到的只有Array对象,而不是底层字节)。在测试3中,每次循环都强制收集一个集合 - 大对象堆被包含在内,所以进程的内存使用量不会增加。


      I am playing around with the garbage collector in C# (or rather the CLR?) trying to better understand memory management in C#.

      I made a small sample program that reads three larger files into a byte[] buffer. I wanted to see, if

      • I actually need to to anything in order to handle memory efficient
      • it has any impact when setting the byte[] to null after the end of the current iteration
      • and finally if it would help when forcing a garbage collection via GC.Collect()

      Disclaimer: I measured memory consumption with windows task manager and rounded it. I tried several times, but overall it remained about the same.

      Here is my simple sample program:

      static void Main(string[] args)
      {
          Loop();
      }
      
      private static void Loop()
      {
          var list = new List<string> 
          { 
              @"C:\Users\Public\Music\Sample Music\Amanda.wma",       // Size: 4.75 MB
              @"C:\Users\Public\Music\Sample Music\Despertar.wma",    // Size: 5.92 MB
              @"C:\Users\Public\Music\Sample Music\Distance.wma",     // Size: 6.31 MB
          };
      
          Console.WriteLine("before loop");
          Console.ReadLine();
      
          foreach (string pathname in list)
          {
              // ... code here ...
      
              Console.WriteLine("in loop");
              Console.ReadLine();
          }
      
          Console.WriteLine(GC.CollectionCount(1));
          Console.WriteLine("end loop");
          Console.ReadLine();
      }
      

      For each test, I only changed the contents of the foreach loop. Then I ran the program, at each Console.ReadLine() I stopped and checked the memory usage of the process in windows task manager. I took notes of the used memory and then continued the program with return (I know about breakpoints ;) ). Just after the end of the loop, I wrote GC.CollectionCount(1) to the console in order to see how often the GC jumped in if at all.


      Results


      Test 1:

      foreach ( ... )
      {
          byte[] buffer = File.ReadAllBytes(pathname);
      
          Console.WriteLine ...
      }
      

      Result (memory used):

      before loop:   9.000 K 
      1. iteration: 13.000 K
      2. iteration: 19.000 K
      3. iteration: 25.000 K
      after loop:   25.000 K
      GC.CollectionCount(1): 2
      


      Test 2:

      foreach ( ... )
      {
          byte[] buffer = File.ReadAllBytes(pathname);
          buffer = null;
      
          Console.WriteLine ...
      }
      

      Result (memory used):

      before loop:   9.000 K 
      1. iteration: 13.000 K
      2. iteration: 14.000 K
      3. iteration: 15.000 K
      after loop:   15.000 K
      GC.CollectionCount(1): 2
      


      Test 3:

      foreach ( ... )
      {
          byte[] buffer = File.ReadAllBytes(pathname);
          buffer = null;
          GC.Collect();
      
          Console.WriteLine ...
      }
      

      Result (memory used):

      before loop:   9.000 K 
      1. iteration:  8.500 K
      2. iteration:  8.600 K
      3. iteration:  8.600 K
      after loop:    8.600 K
      GC.CollectionCount(1): 3
      



      What I dont understand:

      • In Test 1, the memory increases with each iteration. Therefore I guess that the memory is NOT freed at the end of the loop. But the GC still says it collected 2 times (GC.CollectionCount). How so?
      • In Test 2, it obviously helps that buffer is set to null. The memory is lower then in Test 2. But why does GC.CollectionCount output 2 and not 3? And why is the memory usage not as low as in Test 3?
      • Test 3 uses the least memory. I would say it is so because 1. the reference to the memory is removed (buffer is set to null) and therefore when the garbage collector is called via GC.Collect() it can free the memory. Seems pretty clear.

      If anyone with more experience could shed some light on some of the points above, it would really help me. Pretty interesting topic imho.

      解决方案

      Looking at the fact you are reading in entire WMA files into an array, I'd say those array objects are being allocated in the Large Object Heap. This is a seperate heap that's managed in a more malloc-type way (because compacting garbage collection isn't efficient at dealing with large objects).

      Space in the Large Object Heap is collected according to different rules and it doesn't count towards the main generation count and that'll be way you're not seeing a difference in the number of collections between tests 1 and 2 even though the memory is being re-used (all that's being collected there is the Array object, not the underlying bytes). In Test 3 you are forcing a collection each time round the loop - the Large Object Heap is being included in that so the memory useage of the process does not increase.

      这篇关于有人可以解释垃圾收集器的行为吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆