Write it down once Analysis of back-end service memory leakage in a HIS system based on. NET

1: Background

1. Tell a story

That his brother the day before yesterday sql tutorial Come to me again. I gave it to you last time because of the high CPU explosion java basic course It's solved. It seems that you trust me very much. This time it's another journey python basic tutorial Sequence and encounter c# tutorial Memory leak detected, hope vb.net tutorial I'll help diagnose it.

In fact, the elder brother's technology is still very good. Since he can give me dump, he really has a difficult problem 😂😂😂, I have to be prepared 😬😬😬, After communication, the memory of the program will slowly expand until it destroys itself. The problem is such a problem. Next, I will sacrifice my housekeeping tool windbg.

2: windbg analysis

1. Where did it leak?

In many previous articles, I have said that in case of such memory leakage, we must first check whether it is a managed heap or an unmanaged heap? If it is the latter, in most cases, we can only raise our hands and surrender, because the water is too deep... Don't look at those cases, allocate unmanaged memory with the {AllocHGlobal} method, and then use! The reality of Pediatrics that heap went to is much more complicated than this...

Next, use it first! address -summary to see the committed memory of the current process.

0:000> !address -summary

--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
Free                                    345     7dfd`ca3ca000 ( 125.991 TB)           98.43%
<unknown>                             37399      201`54dbf000 (   2.005 TB)  99.83%    1.57%
Heap                                  29887        0`d179b000 (   3.273 GB)   0.16%    0.00%
Image                                  1312        0`0861b000 ( 134.105 MB)   0.01%    0.00%
Stack                                   228        0`06e40000 ( 110.250 MB)   0.01%    0.00%
Other                                    10        0`001d8000 (   1.844 MB)   0.00%    0.00%
TEB                                      76        0`00098000 ( 608.000 kB)   0.00%    0.00%
PEB                                       1        0`00001000 (   4.000 kB)   0.00%    0.00%

--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_MAPPED                              352      200`00a40000 (   2.000 TB)  99.57%    1.56%
MEM_PRIVATE                           67249        2`2cbcb000 (   8.699 GB)   0.42%    0.01%
MEM_IMAGE                              1312        0`0861b000 ( 134.105 MB)   0.01%    0.00%

--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_FREE                                345     7dfd`ca3ca000 ( 125.991 TB)           98.43%
MEM_RESERVE                           11805      200`22ae8000 (   2.001 TB)  99.60%    1.56%
MEM_COMMIT                            57108        2`1313e000 (   8.298 GB)   0.40%    0.01%

From the perspective of divination, the process commits memory MEM_COMMIT = 8.2G, then let's look at the managed heap size and use it! eeheap -gc command.

0:000> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x0000027795928060
generation 1 starts at 0x000002779572F0D0
generation 2 starts at 0x000002763DCE1000

Total Size:              Size: 0xcd28c510 (3442001168) bytes.
------------------------------
GC Heap Size:    Size: 0xcd28c510 (3442001168) bytes.

As can be seen from the last line, the current GC heap Size= 3442001168 /1024/1024/1024 =3.2G, that is, about 8.2G - 3.2G = 5G memory is lost... Nima, a typical unmanaged memory leak. If you really don't open the pot, you may really have to plant it...

2. Look for unmanaged memory leaks

In addition to the GC heap, there is also a process called the loader heap. There are many things in it, including high-frequency heap, low-frequency heap, Stub heap, JIT heap, etc. it stores AppDomain, Module, method descriptor, method table, EEClass and other related information. From experience, this loader heap is a priority for investigating unmanaged leakage. You can use it if you want to view it! eeheap -loader command.

0:000> !eeheap -loader
...
Module 00007ffe2b1b6ca8: Size: 0x0 (0) bytes.
Module 00007ffe2b1b7e80: Size: 0x0 (0) bytes.
Module 00007ffe2b1b9058: Size: 0x0 (0) bytes.
Module 00007ffe2b1ba230: Size: 0x0 (0) bytes.
Module 00007ffe2b1bb408: Size: 0x0 (0) bytes.
Module 00007ffe2b1bc280: Size: 0x0 (0) bytes.
Module 00007ffe2b1bd458: Size: 0x0 (0) bytes.
Module 00007ffe2b1be630: Size: 0x0 (0) bytes.
Module 00007ffe2b1bf808: Size: 0x0 (0) bytes.
Module 00007ffe2b1f0a50: Size: 0x0 (0) bytes.
Module 00007ffe2b1f1c28: Size: 0x0 (0) bytes.
Module 00007ffe2b1f2aa0: Size: 0x0 (0) bytes.
Total size:      Size: 0x0 (0) bytes.
--------------------------------------
Total LoaderHeap size:   Size: 0xc0fb9000 (3237711872) bytes total, 0x5818000 (92372992) bytes wasted.

It's good that you don't lose this command. You're shocked when you lose. It took several minutes for the windbg interface to stop... Two points of information can be obtained from the output:

  • Total occupation of loader heap: 3237711872 /1024/1024/1024 = 3.01G

  • There are a lot of module s. I estimate there are tens of thousands...

To satisfy my curiosity, I decided to write a small script to see how many module s there are???

I go. There are actually 19w modules. No wonder they occupy more than 3 G. I feel that they are not far from the truth. The next question is what these modules are and where they come from???

3. Find the source of module

To find the source, you can think about it carefully. The nesting relationship of modules should be: module - > assembly - > AppDomain, so checking AppDomain may give us more information and use it next! DumpDomain exports all the application domains of the current process. It takes a few minutes to brush. Hey... The screenshot is as follows:

As can be seen from the figure, there are a large number of assemblies of type , Dynamic , and you must want to ask what this means? Yes, this is the Assembly dynamically created by the code, which is as high as 19w... The next problem to be solved is: how are these assemblies created???

4. Export module content

Regular readers should know how I export the problem code from the module. Yes, I'm looking for the startaddress of the module. Here I'll choose one of the modules: 00007ffe2b1f2aa0.

2:2:152> !dumpmodule 00007ffe2b1f2aa0
Name: Unknown Module
Attributes:              Reflection SupportsUpdateableMethods IsDynamic IsInMemory 
Assembly:                000002776c1d8470
BaseAddress:             0000000000000000
PEFile:                  000002776C1D8BF0
ModuleId:                00007FFE2B1F2EB8
ModuleIndex:             00000000000177CF
LoaderHeap:              0000000000000000
TypeDefToMethodTableMap: 00007FFE2B1EE8C0
TypeRefToMethodTableMap: 00007FFE2B1EE8E8
MethodDefToDescMap:      00007FFE2B1EE910
FieldDefToDescMap:       00007FFE2B1EE960
MemberRefToDescMap:      0000000000000000
FileReferencesMap:       00007FFE2B1EEA00
AssemblyReferencesMap:   00007FFE2B1EEA28

I'll go. BaseAddress doesn't have an address. Unfortunately, that means you can't export the module. It's right to think about it. After all, it's dynamically generated. Maybe the code writers don't know what the module is? Is there really no way? But as the saying goes, there is no way out of heaven 😅😅😅, Yes! There is a mt (methodtable) parameter in the dumpmodule command, which is used to display the types in the current module. This is an important clue.

||2:2:152> !dumpmodule -mt 00007ffe2b1f2aa0 
Name: Unknown Module
Attributes:              Reflection SupportsUpdateableMethods IsDynamic IsInMemory 
Assembly:                000002776c1d8470

Types defined in this module

              MT          TypeDef Name
------------------------------------------------------------------------------
00007ffe2b1f3168 0x02000002 <Unloaded Type>
00007ffe2b1f2f60 0x02000003 <Unloaded Type>

Types referenced in this module

              MT            TypeRef Name
------------------------------------------------------------------------------
00007ffdb9f70af0 0x02000001 System.Object
00007ffdbaed3730 0x02000002 Castle.DynamicProxy.IProxyTargetAccessor
00007ffdbaec8f98 0x02000003 Castle.DynamicProxy.ProxyGenerationOptions
00007ffdbaec7fe8 0x02000004 Castle.DynamicProxy.IInterceptor

You can see that two , type s with their method table addresses are defined in the module. Then , mt , is exchanged for , md (method descriptor) to get the content of the final module.

It's finally clear here that the old man used Castle to do an AOP function. It should be that he didn't use AOP correctly, resulting in the generation of a dynamic assembly of {19w +. No wonder he will eventually burst the memory... The root has finally been found. How to modify it next???

5. Modify Castle AOP problem code

I'm embarrassed now. After all, I really haven't played castle 😥😥😥, However, as the old rule, go to bing to see if Tianya has fallen. Hey, there are really articles about Castle AOP causing memory leakage: Castle Windsor Interceptor memory leak , the solution is also provided. The screenshot is as follows:

Quickly throw this link to my brother. I feel I can only help him here. The rest can only depend on fortune.

3: Summary

It's really a trick of fortune. My brother finished it with lightning speed. He finished the self-test online that night.


I quickly asked my brother how to change it 😁😁😁, Brother didn't hesitate to release the source code. Sure enough, set the ProxyGenerator to static according to the foreigner's suggestion... Otherwise, one new and one assembly. Look at the code before the change. The screenshot is as follows:

After solving these two difficult problems, do you think you want to give me a small trophy? 😕😕😕

Keywords: C#

Added by ahasanat on Tue, 18 Jan 2022 21:45:40 +0200