In this article, we will talk about the relationship between the garbage collector and the virtual memory and physical memory of the program. Let's talk about how to determine whether your managed heap is healthy. Why does the program throw an OutofMemoryException when the machine has a large amount of memory.
Relationship between garbage collection and physical memory virtual memory:
If you know this topic well, skip this section.
GC needs to allocate segments. for explanations about the segments, see "efficient garbage collector (1)". GC calls VirtualAlloc to allocate segments. This means that if your process does not have enough continuous space, the allocation will fail. This is a legal situation where GC throws an OutOfMemeoryException (specifically, GC does not throw an exception, and the execution engine does the throw exception. GC only returns NULL when the allocation fails ).
Some people often ask me this question: "Why does my program's hosting heap only use x mb of memory and throw an OutofMemoryException during runtime ?" XMB is much smaller than 2 GB.
Remember that some memory in the. Net program is not consumed by GC. GC, like other things, is competing for virtual memory space. For example, the modules loaded in your process need to occupy virtual memory. Some modules directly call the local code to allocate memory, which also consumes virtual memory (VirtualAlloc, HeapAlloc, C ++ new, and so on ). The CLR itself also has some memory that is not consumed by GC allocation, such as jitted code and some data structures required by CLR. Generally, CLR requires a relatively small amount of memory. You can use SOS! Eeheap command to view the memory used by CLR. The following is an output example for running this command. Some instructions are enclosed in brackets.
0: 119>! EeheapLoader Heap: ------------------------------------ System Domain: 79bbd970LowFrequencyHeap: Size: 0x0 (0) bytes. highFrequencyHeap: 007e0000 () Size: 0x2000 (8192) bytes. stubHeap: 007d0000 (10000: 7000) Size: 0x7000 (28672) bytes. virtual Call Stub Heap: IndcellHeap: Size: 0x0 (0) bytes. lookupHeap: Size: 0x0 (0) bytes. resolveHeap: Size: 0x0 (0) bytes. dispatchHeap: Size: 0x0 (0) bytes. cacheEntryHeap: Size: 0x0 (0) bytes. total size: 0x9000 (36864) bytes ---------------------------------------- Shared Domain: 79bbdf18... total size: 0xe000 (57344) bytes ---------------------------------------- Domain 1: 151a18... total size: 0x147000 (1339392) bytes -------------------------------------- Jit code heap: LoaderCodeHeap: 23980000 (10000: 7000) Size: 0x7000 (28672) bytes .... total size: 0x87000 (552960) bytes [jited code consumes a very small amount of space-this is a very large program] ------------------------------------ Module Thunk heaps: Module 78c40000: Size: 0x0 (0) bytes .... total size: 0x0 (0) bytes ---------------------------------------- Module Lookup Table heaps: Module 78c40000: Size: 0x0 (0) bytes .... total size: 0x0 (0) bytes ------------------------------------------ Total LoaderHeap size: 0x1e5000 (1986560) bytes [total Loader heap takes <2 MB] ============================== ========= Number of GC Heaps: 4 ---------------------------- Heap 0 (0015ad08) generation 0 starts at 0x49521f8cgeneration 1 starts at 0x494d7f64generation 2 starts at 0x007f0038ephemeral segment alcontext location: none [The first 2 segments are read only segments for frozen strings which is why they look a bit odd compared to other segments. the addresses for begin and segment are very different and usually they are tiny segments (unless you have tons and tons of frozen strings)] segment begin allocated size00178250 7a80d84c limit 0x00021980 (137600) 00161918 78c50e40 78c7056c platinum (128812) 007f0000 007f0038 047eed28 0x03ffecf0 (67103984) 3a120000 3a120038 3a3e84f8 large (2917568) 46120000 49e05d04 0x03ce5ccc (46120038) large object heap starts at 0x0000f0038 segment begin allocated memory %%f0038 11ad0008 %( 19791824) 20960000 20960038 224f7970 %( 28932408) Heap Size 0xae65830 (182868016) %heap 1 (0015b688 )... heap Size 0x7f213bc (133305276) ------------------------------ Heap 2 (0015c008 )... heap Size 0x7ada9ac (128821676) ------------------------------ Heap 3 (0015cdc8 )... heap Size 0x764c214 (124043796) ---------------------------- GC Heap Size 0x21ead7ac (569038764) [the memory consumed by the managed Heap is about 540 MB]
The program has an OutOfMemoryException at this moment. Let's take a look at the idle virtual memory block at this time point. You can use it! Vadump command or some other tools to analyze the virtual memory usage. I like it very much! Address command, which can output the largest free zone.
0:119> !address...Largest free region: Base 54000000 - Size 03b600000:119> ? 03b60000Evaluate expression: 62259200 = 03b60000
We can see that the maximum idle area is smaller than 64 MB, which is why OOM occurs. The garbage collector needs to allocate a new segment, but there is not enough space (this program runs in Server GC mode, so a segment is 64 MB ).
If you load or detach modules of different sizes in a program, the virtual memory space will have a lot of fragments, which is very bad. For example, the COM dlls is uninstalled when it is not used, and then loaded again later. The web program has many small dll (a 10 k dll needs to occupy 64 K of virtual memory ).
The garbage collector has nothing to do with the virtual memory except the allocation segment. Physical memory is another thing. As I mentioned earlier, garbage collection is triggered if your machine's physical memory is low. At this time, the garbage collector becomes very aggressive. When the physical memory is low, the ratio of GC performance counters 0, 1, and 2 is close to 1. if the GC does not have enough memory to meet the allocation request, an OutofMemoryException is thrown.
Now the machine has more than 2 GB of memory. It is important not to excessively fragmented the virtual memory. However, if your program runs on a 64-bit server, the virtual memory space is large, and the physical memory may be a limiting factor.
Is your managed heap normal?
Let's take a look at how to obtain the performance data of the managed heap:
1. Collect performance counter Log Data
I recommend that you record data at a very small interval (for example, 1 second) when using the GC performance counter, and then collect data for several consecutive minutes. Generally, this is more meaningful than collecting data for several hours every 5-10 seconds. However, if the problem with your hosting program appears randomly, you have no choice but to collect performance counter data for a long time.
2. Generate a dump file for analysis.
If you want to analyze the issue of hosting heap, you 'd better grasp a complete dump. mini dump is usually useless.
3. Use CLRProfiler to observe
CLRProfiler is a very heavy tool and cannot be attached to a process. For details about how to use this tool, refer to its documentation, which provides detailed instructions. There are several examples for testing.
What kind of data makes sense when you look at hosting a heap?
1. Time in GC
If the value of % Time in GC is small, this parameter is useless for analysis allocation problems. When the value of % Time in GC is very large, it often means that the program has done too many two generations of garbage collection and it takes a long Time each Time. At this time, we should analyze your code in detail to find out why there are so many 2-generation collections, and whether there is a way to change the allocation mode to reduce the time for 2-generation collection.
2. Managed stack Growth Model
If you have a service application that needs to run for a long time. If you observe the continuous growth of the hosting heap, it is usually a bad sign. In general, the service program should clear the data related to each processed request. If this is not the case, your program may have memory leakage, you must check the cause. Otherwise, the program reports an OutofMemoryException. In this case, developers are requested to fix the issue immediately.
When you observe the performance counters, you can also find some obvious problems. For example, if # of GC Handles continues to grow, it indicates that there must be a handle leakage in your program.
3. Ratio of recovery times of each generation
I often tell people that the ratio of a healthy hosting heap 2-generation recycle count to a 1-generation recycle count is. If you find that their ratio is, you need to check your program. If the hosting heap is large, it will take a long time to perform a two-generation recovery. Ideally, we want to do as little as possible for the two-generation recovery.
4. Fragmentation)
Fragment is the free space on the hosting stack. You can view it through the following sos command:
! Dump-type Free-stat
Of course, the smaller the fragments in the program, the better, but this is almost impossible. As long as you use IO or other objects that may have fixed memory addresses, memory fragments may be formed on the managed stack. Fragments on different generations have different effects on program performance:
It is good to have fragments on the zero-generation stack, because we need to allocate objects on the zero-generation stack, which will occupy these fragments when allocating objects. It is ideal that all program fragments appear on the zero-generation stack. You can! The dumpheap command specifies the start and end addresses to check the space on the zero-generation stack. Let's look at the dump in the previous example:
Heap 0 (0015ad08)generation 0 starts at 0x49521f8cgeneration 1 starts at 0x494d7f64generation 2 starts at 0x007f0038ephemeral segment allocation context: nonesegment begin allocated size00178250 7a80d84c 7a82f1cc 0x00021980(137600)00161918 78c50e40 78c7056c 0x0001f72c(128812)007f0000 007f0038 047eed28 0x03ffecf0(67103984)3a120000 3a120038 3a3e84f8 0x002c84c0(2917568)46120000 46120038 49e05d04 0x03ce5ccc(63855820) [the last one is always the ephemeral segment]0:119> ? 49e05d04-0x49521f8cEvaluate expression: 9321848 = 008e3d78 [gen0 is about 9MB]0:119> !dumpheap -type Free -stat 0x49521f8c 49e05d04 ------------------------------Heap 0total 409 objects------------------------------Heap 1total 0 objects------------------------------Heap 2total 0 objects------------------------------Heap 3total 0 objects------------------------------total 409 objectsStatistics: MT Count TotalSize Class Name0015a498 409 7296540 FreeTotal 409 objects
From the above output, we can see that the size of the 0-generation heap is about 9 M, of which 7 M is free space.
It can be said that fragments are designed on the large object stack, because we will not perform fragment on the large object stack. This does not mean that the allocation of objects on the large object stack is the same as that of the NT heap manager. According to the working characteristics of GC, the released objects are adjusted together to form a large space to meet the large object allocation request.
The worst case is that fragments occur on two-generation and One-generation stacks. If there is still a lot of idle memory on the first and second generations of stacks after a garbage collection, it is certain that the program is faulty in the use of managed memory. If this problem occurs in your program, please refer to "3. Make the Garbage Collector work efficiently".
For a 2-generation heap, it can be considered very good if the fragmentation ratio is less than 20%.
Original article: http://blogs.msdn.com/ B /maoni/archive/2005/05/06/415296.aspx
Original Author: Maoni Stephen ens
Related essays:
Efficient garbage collector (3)
Make the Garbage Collector work efficiently (2)
Make the Garbage Collector work efficiently (1)
. Net garbage collection mechanism principle (1)
. Net garbage collection mechanism principle (2)
. Net garbage collection and big object processing