Can someone explain the behavior of the garbage collector?

I play with a garbage collector in C # (or rather CLR?), Trying to better understand memory management in C #.

I made a small trial program that reads three large files into the byte[] buffer. I would like to see if

  • I really need anything to work efficiently with memory
  • this has any effect when setting byte[] to null after the end of the current iteration
  • and finally, if that helps, if you force the garbage collection through GC.Collect()

Disclaimer: I measured memory consumption using the 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 just changed the contents of the foreach . Then I started the program, at each Console.ReadLine() I stopped and checked the memory usage in the process in the Windows task manager. I took notes about the memory used, and then continued with the return program (I know about breakpoints;)). Right after the loop ended, I wrote GC.CollectionCount(1) to the console to see how often the GC jumped, 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 do not understand:

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

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

+6
garbage-collection c #
source share
4 answers

Looking at the fact that you are reading entire WMA files into an array, I would say that these array objects are allocated in a bunch of large objects. This is a separate heap that is managed in a more malloc-style way (since compact garbage collection is inefficient when working with large objects).

The space in the large heap of the object is collected in accordance with different rules, and it is not taken into account in the calculation of the main generation, and it will be that you do not see the difference in the number of collections between tests 1 and 2 even though the memory is reused (everything that is collected, there is an Array object, not base bytes). In test 3, each time you force a collection of loops - a large bunch of objects are included in it, so the memory usage of the process does not increase.

+6
source share

TaskManager is not the best tool for this. Use the CLR Profiler or for something simple, use WriteLine to show GC.GetTotalMemory() .

The main goal of GC is to highlight and highlight a large number of small objects. If you want to learn it, write something that creates a lot of (small) lines or so. Make sure you know what Generational GC means.

The current experiment uses a bunch of large objects (LOH), which contains a number of rules and problems.

+2
source share

Give a link that I find useful to you.

http://msdn.microsoft.com/en-us/magazine/ee309515.aspx

-Joe Yu

+1
source share

Using memory to view through the task manager for the process. Remember that the CLR manages memory on behalf of your application, so you usually will not see the use of the GC heap, reflected directly in the use of process memory.

Allocating and freeing memory is not free, so the CLR will try to optimize this to lower the cost. Thus, when objects are collected from the heap, you may or may not see the memory released in the OS.

0
source share

All Articles