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? 😕😕😕