首页 > 系统相关 >内存高【3】

内存高【3】

时间:2022-10-27 23:56:08浏览次数:51  
标签:00000000 UNKNOWN System FastReport Export 内存

一个关于内存溢出的现象 

windbg 分析

1. 找出异常对象

如果内存溢出了,大家应该知道 C# 会抛一个 OutOfMemoryException 异常,而且还会附加到那个执行线程上,所以先用 !t 命令调出当前的所有托管线程。

0:000> !t
ThreadCount:      17
UnstartedThread:  0
BackgroundThread: 12
PendingThread:    0
DeadThread:       4
Hosted Runtime:   no
                                                                         Lock  
       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)

可以清楚的看到,0号 线程果然带了一个 System.OutOfMemoryException,接下来用 !pe 查查这个异常的调用栈信息。

0:000> !pe 57b53d90

Exception object: 57b53d90 Exception type: System.OutOfMemoryException Message: 没有足够的内存继续执行程序。 InnerException: <none> 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

 

从上面的调用栈可以看出,貌似程序是在做一个 pdf 打印,最后在 Marshal.AllocHGlobal 上抛了异常,熟悉这个方法的朋友应该知道,它就是用来分配 非托管内存 的。。。 情况貌似有点不妙。

接下来用 ILSpy 查一下 AllocHGlobal 方法的源码,看看有什么可挖掘的地方。

 

 

 

从图中源码逻辑可以看出,一旦非托管内存分配失败,托管层上手工抛出 OutOfMemoryException 异常,我去,这难道是非托管内存溢出啦

真的是非托管溢出了吗?

要鉴别是否为非托管堆出的问题,还是用那个老办法,看看 MEM_COMMIT Size ≈ GC Heap Size 即可。

  • 用 !address -summary 查看进程的内存使用量
0:000> !address -summary

--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
<unknown>                             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%

 

从上面的 MEM_COMMIT 指标可以看出内存使用量为 1.28 G

  • 用 !gcheap -gc 看看托管堆的大小
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.

从最后一行可以看出托管堆占用了 425453488/1024/1024 = 405M

也就是说大概 800M 不知道哪里去了,看似有点吓人,其实算算也还可以,这里我稍微补充一下,看下面的公式:

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

 从上面列出来的信息可以看出,最后累积出的 918M 和 内存使用量 1.28G 差不了多少,有些朋友可能要问, 这个 GCLoader 怎么算出来的,

很简单,它是 CLR 的加载堆,使用 !eeheap -loader 即可。

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

到这里,我陷入了僵局,才 1.28G 的内存占用,怎么就会把程序给弄溢出了? 既然内存上看不出问题,那就从线程上入手吧

查看每个线程都在做什么?

要想看线程,可以用 ~*e !clrstack 调出所有线程的托管栈,突然我发现主线程有点奇怪,调用栈特别深,不信我截图跟你看

 

 

 从图中可以看到,xxx.xxx.PrintOrder2Pdf.Form1.timer1_Tick 高达 133 个,这说明 Form 窗体上有一个 timer 没有控制好,出现重复执行的情况了,不管怎么说,这个地方肯定有问题,接下来要做的就是把这个 timer1_Tick 源码导出来看看怎么写的,还是用那个 !name2ee + !savemodule 老命令导出,代码简化如下。

private void timer1_Tick(object sender, EventArgs e)
{
    if (!IsContinue)
    {
        PrintMsg("等待上一扫描执行完毕");
        IsContinue = true;
        return;
    }
    IsContinue = false;
    GetPatList();
    if (PatList == null || PatList.Rows.Count == 0)
    {
        timer1.Interval = 600000;
        PrintMsg("xxxx");
        IsContinue = true;
        return;
    }
    for (int i = 0; i < PatList.Rows.Count; i++)
        {
          xxx
        }
    IsContinue=true;
}

从代码中可以看出,这个方法用了很多的 IsContinue 来踢掉重复请求,但最终还是出了bug,导致无限量递归,跟朋友沟通后建议用 Stop() 和 Start() 来处理,参考如下代码:

 

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

            timer1.Tick += Timer1_Tick;

            timer1.Start();
        }

        private void Timer1_Tick(object sender, EventArgs e)
        {
            timer1.Stop();
            MessageBox.Show("hello");
            timer1.Start();
        }

起码这种 停止 再 启动 的方式肯定能规避timer的重复执行,先把这个改了再说,给医院那边先部署上,再观后效。。

 

标签:00000000,UNKNOWN,System,FastReport,Export,内存
From: https://www.cnblogs.com/wwkk/p/16834435.html

相关文章

  • 创建对象内存分析
     测试代码packagecom.oop.demo2;publicclassApplication{publicstaticvoidmain(String[]args){Petdog=newPet();//当代码输出这一行时,name=nul......
  • 存储器 主存 内存条 总线宽度
    存储器基本概念主存的基本结构主存的性能指标半导体存储器组织内存条与总线宽度CPU与主存模块的连接及读写操作 ......
  • 2、计算Java对象所占内存的大小
    当一个对象有多个属性,需要计算整个对象的大小时,可以借助org.apache.lucene工具类首先引入maven依赖<dependency><groupId>org.apache.lucene</groupId><artifac......
  • Linux 内存管理
    Linux操作系统采用了哪种方式来管理内存呢?在回答这个问题前,我们得先看看Intel处理器的发展历史。早期Intel的处理器从80286开始使用的是段式内存管理。但是很快发现,......
  • 内存总结
    为了在多进程环境下,使得进程之间的内存地址不受影响,相互隔离,于是操作系统就为每个进程独立分配一套虚拟地址空间,每个程序只关心自己的虚拟地址就可以,实际上大家的虚拟地址都......
  • 内存满了,会发生什么?
    1、内存分配的过程是怎样的?应用程序通过malloc函数(全称是memoryallocation,中文叫动态内存分配)申请内存的时候,实际上申请的是虚拟内存,此时并不会分配物理内存。当应用程序......
  • 计算中文在内存占用的字节数
    #include<iostream>usingnamespacestd;voidmain(){ //功能:计算中文在内存占用的字节数 charname[]="成都"; charnode_name[]="成都移动04节点"; co......
  • Java查询服务器(windows + linux)和系统各个指标数据 cpu、内存、硬盘、线程、请求错
    importjava.io.BufferedReader;importjava.io.File;importjava.io.IOException;importjava.io.InputStreamReader;importjava.lang.management.ManagementFactory......
  • 关于eclpse 打不开 打开闪退 占用后台内存的问题
    场景描述:eclipse打开了多个无用的工作区,重新打开另一个eclipse窗口,另一个窗口还没打开之前,关闭当前的工作区,等了很久eclipse没反应,于是重新打开eclipse,重新打开闪退......
  • 关于使用 uni-app 开发小程序时,出现的父子组件传值时,Object 类型的 props,内存地址不同
    太坑了家人扪,谁能想到,在H5正常运行的父子组件props传值,会在小程序出现问题啊!这周,咱上头让俺把原本基于H5开发的网站,改成小程序,还好这也不是第一次了,咱经验丰富,项目......