A memory overflow analysis of. Net hospital CIS system


1: Background

1. Tell a story

A few days ago, a friend, Jia Wx, asked for help and said that his program always ran out of memory recently and crashed, as shown in the following figure:

After chatting with this friend, I found that he is also engaged in medical treatment. Ha ha,. Net still has a great market in medical treatment. However, for memory problems, I have to pray first that it should not be unmanaged…

Don’t talk too much nonsense. Go to WinDbg and see if you can save it first.

2: WinDbg analysis

1. Find out the abnormal object

If the memory overflows, you should know that C # will throw oneOutOfMemoryExceptionException, and it will be attached to the execution thread, so use!tCommand to call out all currently managed threads.

0:000> !t
ThreadCount:      17
UnstartedThread:  0
BackgroundThread: 12
PendingThread:    0
DeadThread:       4
Hosted Runtime:   no
       ID OSID ThreadOBJ    State GC Mode     GC Alloc Context  Domain   Count Apt Exception
   0    1 16b0 007da908     26020 Preemptive  64EDD188:00000000 00823830 1     STA System.OutOfMemoryException 57b53d90
   2    2  af8 007e9dc8     2b220 Preemptive  00000000:00000000 007d4838 0     MTA (Finalizer) 
   3    3 1d94 0081af28     21220 Preemptive  00000000:00000000 007d4838 0     Ukn 
   5    6 246c 0772b960   102a220 Preemptive  00000000:00000000 007d4838 0     MTA (Threadpool Worker) 
   8   47 277c 2eebf038   8029220 Preemptive  00000000:00000000 007d4838 0     MTA (Threadpool Completion Port) 
XXXX   41    0 2eebf580   1039820 Preemptive  00000000:00000000 007d4838 0     Ukn (Threadpool Worker)

You can see clearly that,No.0The thread did bring oneSystem.OutOfMemoryExceptionNext, use!peCheck the call stack information for this exception.

0:000> !pe 57b53d90
Exception object: 57b53d90
Exception type:   System.OutOfMemoryException
Message: there is not enough memory to continue the program.
StackTrace (generated):
    SP       IP       Function
    00482C80 6450BD46 mscorlib_ni!System.Runtime.InteropServices.Marshal.AllocHGlobal(IntPtr)+0xc2fdf6
    00482CB0 198DCEF2 UNKNOWN!FastReport.Export.TTF.TrueTypeCollection..ctor(System.Drawing.Font)+0xe2
    00482D00 198DCC0F UNKNOWN!FastReport.Export.TTF.ExportTTFFont.GetFontData()+0x47
    00482D58 198DAD54 UNKNOWN!FastReport.Export.Pdf.PDFExport.WriteFont(FastReport.Export.TTF.ExportTTFFont)+0xa4
    00483A7C 198D9CD5 UNKNOWN!FastReport.Export.Pdf.PDFExport.AddPDFFooter()+0x8d
    00483C38 198D9B53 UNKNOWN!FastReport.Export.Pdf.PDFExport.Finish()+0x23
    00483C80 19938119 UNKNOWN!FastReport.Export.ExportBase.Export(FastReport.Report, System.IO.Stream)+0x229
    00483CD8 19937A9D UNKNOWN!FastReport.Export.ExportBase.Export(FastReport.Report, System.String)+0x4d
    00483D08 19937A3D UNKNOWN!FastReport.Report.Export(FastReport.Export.ExportBase, System.String)+0xd
    00483D10 15D9FA39 UNKNOWN!xxxx.xxx.FormPrint.PrintPdf(Boolean, System.String, xxxx.DAL.xxx.DataObject.IPatinfoBase, Boolean, System.String)+0x359
    00483DF0 137B265A UNKNOWN!xxxx.UI.xxx.PrintOrdert2PDF.Handle(System.Object[])+0x3ca
    00483EB4 1178B36C xxx_PrintOrder2Pdf!xxxx.xxx.PrintOrder2Pdf.Form1.timer1_Tick(System.Object, System.EventArgs)+0xca4
    0048414C 117884DD UNKNOWN!System.Windows.Forms.Timer.OnTick(System.EventArgs)+0x15
    00484154 117883A0 UNKNOWN!System.Windows.Forms.Timer+TimerNativeWindow.WndProc(System.Windows.Forms.Message ByRef)+0x38
    00484160 07C939B7 UNKNOWN!System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)+0x5f

As can be seen from the call stack above, it seems that the program is doing a PDF printing, and finally in theMarshal.AllocHGlobalAn exception has been thrown. Those who are familiar with this method should know that it is used to allocateUnmanaged memoryYes… There seems to be something wrong.

Next, useILSpyCheck the source code of allochglobal method to see what can be mined.

As can be seen from the source code logic in the figure, once the unmanaged memory allocation fails, it will be thrown manually on the managed layerOutOfMemoryExceptionException, I go, is this an unmanaged memory overflow???

2. Is it really an unmanaged overflow?

To identify whether there is a problem with the unmanaged heap, we should use the old method to seeMEM_COMMIT Size ≈ GC Heap SizeThat’s it.

  • use!address -summaryView the memory usage of the process
0:000> !address -summary

--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
                             16334          460bb000 (   1.094 GB)  78.00%   54.72%
Free                                  11177          26319000 ( 611.098 MB)           29.84%
Image                                   831           e48e000 ( 228.555 MB)  15.91%   11.16%
Heap                                    184           4547000 (  69.277 MB)   4.82%    3.38%
Stack                                    61           11c0000 (  17.750 MB)   1.24%    0.87%
Other                                    10             60000 ( 384.000 kB)   0.03%    0.02%
TEB                                      20             24000 ( 144.000 kB)   0.01%    0.01%
PEB                                       1              3000 (  12.000 kB)   0.00%    0.00%

--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_COMMIT                            16213          521bd000 (   1.283 GB)  91.43%   64.15%
MEM_FREE                              11177          26319000 ( 611.098 MB)           29.84%
MEM_RESERVE                            1228           7b1a000 ( 123.102 MB)   8.57%    6.01%

From aboveMEM_COMMITThe indicator shows that the memory usage is1.28 G

  • use!gcheap -gcLook at the size of the managed heap
0:000> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x64c534f8
generation 1 starts at 0x64bccb84
generation 2 starts at 0x02531000
ephemeral segment allocation context: none

GC Heap Size:    Size: 0x195be7b0 (425453488) bytes.

You can see from the last line that the managed heap is occupied425453488/1024/1024 = 405M

That is to say, I don’t know where the 800m is. It seems a little scary, but the calculation is OK. Let me add a little here and see the following formula:

MEM_COMMIT (1.28G) = Image (228M) + Heap (69M) + Stack (18M) + GCHeap(450M) + GCLoader (153M) + else = 918M

As can be seen from the information listed above, the accumulated918MAnd memory usage1.28GIt’s not much different. Some friends may want to ask this questionGCLoaderHow to calculate out, very simple, it is the CLR load heap, using!eeheap -loaderThat’s it.

0:000> !eeheap -loader
Total LoaderHeap size:   Size: 0x995a000 (160800768) bytes total, 0x13e000 (1302528) bytes wasted.

Here, I’m stuck in a stalemate, and I’m ready to go1.28GHow can the program be overflowed when the memory is occupied? Since there is no problem with memory, let’s start with threads to see what they are doing?

3. Check what each thread is doing?

To see threads, you can use~*e !clrstackCall out the managed stack of all threads. Suddenly, I find that the main thread is a little strange. The call stack is very deep. I’ll show you the screenshot if you don’t believe me.

As you can see from the figure,xxx.xxx.PrintOrder2Pdf.Form1.timer1_TickThe number is as high as 133, which indicates that there is a timer on the form that is not well controlled and is repeatedly executed. Anyway, there must be a problem in this place. The next thing to do is to change ittimer1_TickSource export to see how to write, or with that!name2ee + !savemoduleThe old command is exported, and the code is simplified as follows.

private void timer1_Tick(object sender, EventArgs e)
	if (!IsContinue)
		Printmsg ("waiting for the last scan to finish");
		IsContinue = true;
	IsContinue = false;
	if (PatList == null || PatList.Rows.Count == 0)
		timer1.Interval = 600000;
		IsContinue = true;
	for (int i = 0; i < PatList.Rows.Count; i++)

As you can see from the code, this method takes a lot of timeIsContinueTo kick out repeated requests, but eventually there was a bug, leading to unlimited recursion. It is recommended to use it after communicating with friendsStop()andStart()Refer to the following code:

private void button1_Click(object sender, EventArgs e)
            timer1.Interval = 2000;

            timer1.Tick += Timer1_Tick;


        private void Timer1_Tick(object sender, EventArgs e)

At leaststop itagainstart-upThis method can definitely avoid the repeated execution of timer. First change this one, and then deploy it to the hospital, and then see the aftereffect…

3: Summary

After May Day, that is, the day before yesterday, my friend gave a plan to the hospital. Yesterday, there was no problem again. Take a picture to prove it.

As you can see, I have no bottom in my heart… After further communication with friends, we found three pieces of information:

  • The doctor’s computer configuration is 8g or 12g

  • Sometimes, for convenience, the doctor will prescribe a double procedure

  • There are more memory overflow cases of other modules

Look at the next program is to use plug-in programming, but also usedDevExpress + FastReportThese heavyweight components, coupled with the doctor’s dual process, make the computer’s remaining barren memory even more tight. Maybe this is the program in the process1.2GAs for the deep reason why unmanaged memory can not be allocated, the field situation should be more complicated, and this is the only way.

The suggested measures are as follows, very simple.

  • Add the computer configuration, up to 16g is the best, after all, Party A is not poor money

More high quality dry goods: see my GitHub:dotnetfly


Recommended Today

Java Engineer Interview Questions

The content covers: Java, mybatis, zookeeper, Dubbo, elasticsearch, memcached, redis, mysql, spring, spring boot, springcloud, rabbitmq, Kafka, Linux, etcMybatis interview questions1. What is mybatis?1. Mybatis is a semi ORM (object relational mapping) framework. It encapsulates JDBC internally. During development, you only need to pay attention to the SQL statement itself, and you don’t need to […]