Garbage collection in C# not carried out. Why?

后端 未结 5 1999
感动是毒
感动是毒 2021-02-12 03:58

I have tried a simple experiment to verify the functionality of the garbage collector. Referencing 3.9 Automatic memory management (MSDN) about automatic memory managem

5条回答
  •  南方客
    南方客 (楼主)
    2021-02-12 04:14

    Some of the information is already included in the article you link to. There are several indications that the behavior you observe is correct:

    ... the garbage collector may (but is not required to) treat the object as no longer in use.

    ... at some unspecified later time ...

    GC.Collect()

    One important thing, at least for the old (non-concurrent) version of the Garbage collector is, that the garbage collector runs on a different thread. You can verify that in the debugger:

    0:003> !threads
    ThreadCount: 2
    UnstartedThread: 0
    BackgroundThread: 1
    PendingThread: 0
    DeadThread: 0
    Hosted Runtime: no
                                          PreEmptive   GC Alloc           Lock
           ID OSID ThreadOBJ    State     GC       Context       Domain   Count APT Exception
       0    1 1b08 0058f218      a020 Enabled  025553ac:02555fe8 0058b868     1 MTA
       2    2 1e9c 005a78c8      b220 Enabled  00000000:00000000 0058b868     0 MTA (Finalizer)
    

    The Finalizer thread performs the garbage collection. All other threads are suspended during the operation, so that no thread can modify objects during the time of reorganization.

    But why is that important?

    It explains why the garbage collection does not apply immediately, neither in your scenario nor if you call GC.Collect() to do the garbage collection. For the garbage collector to run, you also need a thread switch. So, the minimum code needed for a non-concurrent garbage collection is

    GC.Collect();
    Thread.Sleep(0);
    

    If you're concerned about memory management, be sure to also check out the awesome answer about IDisposable.

    Free memory

    Also, nobody has explained yet, that looking at the memory consumption with Task Manager is not reliable.

    .NET acts directly on virtual memory, i.e. uses the virtual memory manager. It does not use the heap, i.e. the heap manager. Instead it uses it's own memory management, called managed heap.

    .NET gets the memory from Windows (the kernel). Assume it gets a fresh piece of memory from Windows, which has no .NET objects inside. From Windows' point of view, the memory is gone (given to .NET). However, from .NET point of view, it's free and can be used by objects.

    Again, you can observe that in the debugger:

    0:003> !address -summary
    --- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
    Free                                     60          71cb9000 (   1.778 Gb)           88.91%
                                    84           986f000 ( 152.434 Mb)  67.09%    7.44%
    Image                                   189           2970000 (  41.438 Mb)  18.24%    2.02%
    ...
    

    What is reported as is virtual memory from Windows point of view. In this case, 150 MB are used.

    0:003>!dumpheap -stat
    ...
    00672208       32      8572000      Free
    ...
    

    So you can see that 8.5 MB are free from .NET point of view, but have not been given back to Windows (yet) and will still be reported as used there.

    Measuring working set

    If you have not modified Task Manager's default column settings, it's even worse, because it will show the Working Set, which is memory in RAM only. However, some of the memory may have been swapped to disk, thus it may not be reported by Task Manager.

提交回复
热议问题