版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、内存泄漏检测方法对于不同的程序可以使用不同的方法来进行内存泄漏的检查,还可以使用一些专门的工具来进行内存问题的检查,例如 MemProof、AQTime、Purify、BundsChecker等。也可以使用简单的办法:利用 Windows 自带的 Perfmon 来监控程序进程的 handlecount、Virtual Bytes 和 Working Set 3 个计数器。Handle Count 记录了进程当前打开的句柄个数,监视这个计数器有助于发现程序是否存在句柄类型的内存泄漏;Virtual Bytes 记录了程序进程在虚拟地址空间上使用的虚拟内存的大小,Virtual Bytes 一般
2、总大于程序的 Working Set,监视 Virtual Bytes 可以帮助发现一些系统底层的问题;Working Set 记录了操作系统为程序进程分配的内存总量,如果这个值不断地持续增加,而Virtual Bytes 却跳跃式地增加,则很可能存在内存泄漏问题。堆栈内存泄漏堆栈空间不足会导致在受托管的情况下引发StackOverflowException 类型的异常,线程泄漏是堆栈内存泄漏的其中一种。线程发生泄漏,从而使线程的整个堆栈发生泄漏。如果应用程序为了执行后台工作而创建了大量的工作线程,但却没有正常终止这些线程,则可能会引起线程泄漏。一个堆栈内存泄漏的例子:private void
3、 button1_Click(object sender, EventArgs e)/ 循环启动多个线程for (int i = 0; i 1500; i+)Thread t = new Thread(new ThreadStart(ThreadProc); t.Start();static void ThreadProc()Console.WriteLine(启动Thread#0,Thread.CurrentThread.ManagedThreadId);/ 阻塞直到当前线程结束Thread.CurrentThread.Join();利用 Perfmon 检测线程堆栈泄漏默认堆栈大小为 1M
4、B,因此如果应用程序的 Private Bytes 不断增大,同时.NET CLR LocksAndThreads 中的 # of current logical Threads 也相应地增大,那么就很可能是发生了线程堆栈泄漏。可以利用 Perfmon 来判断是否存在内存泄漏现象。执行被测试程序的相关操作,并在性能监视器中密切注意“Private Bytes”和“# of current logical Threads”两个计数器的变化曲线,如果 Private Bytes 不断增大,同时# of current logicalThreads 也相应地增大,则可判断程序发生了线程堆栈泄漏。用
5、CLRProfiler 定位线程泄漏代码利用 CLRProfiler 可以帮助检查程序是否存启动 CLRProfiler单击“Start Application”按钮选择需要测试的应用程序,单击“打开”按钮。CLRProfiler 会自动打开被测试程序,执行程序的相关操作,然后单击 CLRProfiler 的“Show Heap Now”按钮说明:这个界面显示了程序的所有堆分配的情况。其中可以看到线程类中分配了 82K,占了 18%以上,其中包含 1500 个线程对象。选中“Threading.Thread”的节点,单击右键,选择“Show Who Allocated”说明:在这个界面中可以看
6、到是哪个类的哪个方法创建了这么多的线程对象,在这里可以看程泄漏。方法如下:到是由 button1_Click 方法调用了线程类,从而定位到线程泄漏的代码。资源泄漏资源通常指系统的对象。例如 GDI 对象句柄、内存句柄等,在编程过程中,使用到很多这些资源对象,但是没有及时地掉就造成了资源泄漏。GDI 泄漏是指程序申请了 GDI 句柄,但是没有及时GDI 泄漏可能导致系统不稳定,或者出现花屏。,导致 GDI 句柄不断累积。一个 GDI 泄漏的例子 :Form1:/调用 Form2 窗体Form2 f = new Form2();/ 显示 Form2 窗体f.ShowDialog();Form2:p
7、rivate void Form2_Load(object sender, EventArgs e)/ 使用 pictureBox 控件加载并显示一个图片pictureBox1.Image = Image.FromFile(picture.JPG);private void Form2_FormClosing(object sender, FormClosingEventArgs e)/ 如果少了这句,则会发生 GDI 资源泄漏/pictureBox1.Image.Dispose();用 Windows 任务管理器协助检测GDI 泄漏对于上面的 GDI 泄漏代码,可以利用Windows 的任务
8、管理器来协助检测。方法如下:首先打开 Windows 任务管理器选择菜单“查看 | 选择列”,出现如图 15.13 所示界面。确保“GDI 对象”被勾选上,然后单击“确定”按钮。启动被测试程序 ResourceLeak(即上面的代码例子的可执行程序),并在 Windows 任务管理器中定位到被测试程序的进程记下应用程序进程的当前 GDI 对象数,然后运行程序的各项操作,在操作过程中密切关注其 GDI 对象数的变化,例如,对于 ResourceLeak.exe 进程,当前的 GDI 对象数是 33,如果点击 button1,程序将调出第二个窗口,窗口加载了一个图片,这个过程会向系统申请一些 GD
9、I 对象资源,因此查看Windows 任务管理器可以看到其 GDI 对象数的变化这时候,把第二个窗口关闭,如果程序存在资源泄漏,则 GDI 对象数不会减少到 33。而且反复操作程序,调出第二个窗口再关闭,可看到 GDI 对象数不断地增加,这样就可判断程序存在 GDI 资源泄漏的现象。利用 GdiUsage 检查 GDI 泄漏GdiUsage 是 Christophe Nasarre 写的一个专门用于检查程序使用 GDI 资源情况的小工具它的使用方法也很简单,具体使用方法如下:首先在上面的输入框输入需要测试的程序路径,然后按“Start”按钮启动被测试程序,程序被启动的同时,GdiUsage 会
10、显示一个“Debuggee Output”窗口,用于展示程序加载的 DLL名称以及地址启动程序后,在 GdiUsage 中单击“Take Snapshots”按钮,给当前程序使用的 GDI 资源情况取一个“快照”可看到当前程序使用到 1 个 Bitmap 类型的 GDI 对象,单击“Details”按钮,还可以看到详细的资源展示界面接着操作被测试程序(单击 ResouceLeark 程序的 button1 按钮),再单击一下“Take Snapshots”按钮,给当前程序使用的 GDI 资源情况取一个“快照”可以看到当前程序使用的 Bitmap 对象增加到 2 个,Region 对象增加 1
11、个。这时关闭 ResouceLeark 程序的 Form2 窗口,再取一个快照,则发现 Bitmap 对象和 Region 对象的个数都未减少,并且如果重复这个过程,Bitmap 对象的个数会不断地增加。因此可以认为程序存在资源泄漏的现象。GDI 与 GDI+1、概述GDI 在全称是 Graphics Device Interface,即图形设备接口。是图形显示与实际物理设备之间的桥梁。GDI 接口是基于函数,虽然使程序员省力不少,但是编程方式依然显得麻烦。例如显示一张位图,我们需要进行“创建位图,读取位图文件信息,启用场景设备,调色板变化“等一系列操作。然而有了 GDI+,繁琐的步骤再次被简
12、化。顾名思义,GDI+就是GDI 的增强版,它是微软在 Windows 2000 以后操作系统中提供的新接口。2、GDI+主要功能GDI+主要提供以下三种功能:(1) 二维矢量图形:GDI+提供了存储图形基元自身信息的类(或结构体)、存储图形基元绘制方式信息的类以及实际进行绘制的类;(2) 图像处理:大多数图片都难以划定为直线和曲线的集合,无法使用二维矢量图形方式进行处理。因此,GDI+为我们提供了 Bitmap、Image 等类,它们可用于显示、操作和保存 BMP、JPG、GIF 等图像格式。(3) 文字显示:GDI+支持使用各种字体、字号和样式来显示文本。相比于 GDI,GDI+是基于 C
13、+类的对象化的应用程序接口,因此用起来更为简单。GDI 的核心是设备上下文,GDI 函数都依赖于设备上下文句柄,其编程方式是基于句柄的;GDI+无需时刻依赖于句柄或设备上下文,用户只需创建一个 Graphics 对象,就可以用面向对象的方式调用其成员函数进行图形操作,编程方式是基于对象的。3、GDI 绘制实例GDI 在使用设备上下文绘制线条之前,必须先调用 SelectObject 以使笔对象和设备上下文关联。其后,在设备上下文中绘制的所有线条均使用该笔,直到选择另一支不同的笔为止。使用 GDI 画线代码如下/ TODO: Add your command handler code here
14、CClientDC clientDC;/目标DCCPen pen (PS_SOLID, 1, RGB(0, 0, 255);clientDC.SelectObject(pen.GetSafeHandle();/开始绘制 clientDC.MoveTo(0, 0)clientDC.LineTo(rect.right, 0); clientDC.SelectObject(oldObject);从上述代码可以看出:在 GDI 编程中,几乎所有的操作都围绕设备上下文 dc 展开。的确,这正是 GDI 编程的特点!设备上下文是 Windows 使用的一种结构,所有 GDI 操作前都需取得特定设备的上下文
15、,函数中的 CClientDC dc (this) 语句完成这一功能。利用 GDI进行图形、图像处理的一般操作步骤为:1. 取得指定窗口的 DC。2. 确定使用的坐标系及映射方式。3. 进行图形、图像或文字处理。4. 释放所使用的DC。但是,在 GDI+中,只需将 Pen 对象直接作为参数传递给 Graphics 类的DrawLine 等方法即可,而不必使 Pen 对象与Graphics 对象关联。4、GDI+绘制实例使用 GDI+画线代码如下/ TODO: Add your command handler code hereCClientDC clientDC (this);/创建 Grap
16、hics 对象Graphics graphics(clientDC);/创建 pen Pen myPen;myPen.SetWidth(1);/画 X 轴myPen.SetColor(Color:Blue); graphics.DrawLine(&myPen, 0, 0, rect.right, 0);创建 Graphics 对象:Graphics 对象表示 GDI+绘图表面,是用于创建图形图像的对象。使用 Graphics 对象绘制线条和形状、呈现文本或显示与操作图像。GDI+的相对与 GDI 而言,新增了一系列功能:渐变的画刷(Gradient Brushes)、基数样条函数(Cardin
17、al Splines)、持久的路径对象(Persistent Path Objects)、变形和矩阵对象 (Transformations &Matrix Object)、可伸缩区域(Scalable Regions)、Alpha 混合(Alpha Blending)和丰富的图像格式支持等。下面,我们来逐个用实际代码实现 GDI+的新增功能。非托管资源造成的内存泄漏在.NET 开发中容易被忽视,引起内存泄漏通常存在于以下三种情况:对象被引用而没有被释放没有释放非托管资源没有释放非托管资源封装对象上个章节描述的 EventHanlder 和 Delegate 造成的内存泄漏就属于第一类,之所以要
18、单独拿出来叙述是因为.NET 事件和代理造成的内存泄漏相对于静态变量的根化引用更容易被忽略且不好被识别出来。第 2 类和第 3 类同属于系统资源类型造成的内存泄漏,但是又有所区别。第二类是指通过本地 API 函数与托管对象进行交互(比如:通过 P/Invoke 方式调用本地 DLL,DLLImport 声明静态外部函数和 COM Interop)所用到的非托管资源。例如:当通过 DLL Import 调用 API 函数 GetDC 函数时忘了调用 ReleaseDC 去释放设备句柄造成 4 个字节的内存泄漏。再如:智能文档中使用的 Word 以及导出EXCEl 功能用到的Office 的COM
19、 非托管组件,在关闭时 GC 不能识别 COM 组件而造成有时候无法对 COM 对象进行释放,这时候可以通过以下两个 InteropServices 函数进行释放System.Runtime.InteropServices.System.Runtime.InteropServices.MarshObject(comObject);Object(comObject);上次在敏捷交流了内存相关事项问题后,给大家留了几道思考题,其中第一道题是“数据库连接 SqlConnection 是不是非托管资源,为什么?”,有些人的回答是“肯定”,之所以有这样回答是因为大家所了解的非托管资源的经典认知就是数据库
20、连接、文件、网络连接都是非托管资源,有人认为 SqlConnection 就是数据库连接,其实不然,.NET 对某些非托管资源提供一种包装类,SqlConnection 就是这种,包装类的源(WrapSource)才真正是托管资源,它管理了非托管资源,而它本身确实托管的。.NET GDI Plus 中常用的Drawing 命名空间下的类很多就是这种包装类型,现将常用的几种非托管包装类列举如下:识别 这种 包装 类型 的主 要方 法就是通过 MSDN 查询是否 该对 象继 承于System.MarshalByRefObject 类。做一个实验来测试 Graphics 的释放,新建一个 Form
21、对象,在 Form 对象的 Paint 事件里,写入以下代码,用于在 Form2 上绘制。运行起来发现,不停的移动 Form2,对应刷新 Form2 的 Paint 事件,在内存管理器里面可以看到实验程序的内存会不停的增长,这说明这时候已经产生了内存泄漏了。启动 AQTime,并启用 Resource Profiler 调试方案,运行程序,隔一段时间调用“Get Result”收集数据。第一次收集数据,GpGrahics 对象的LiveCount =3;第二次收集数据,GpGrahics 对象的LiveCount =21;GpGraphics 对象的持续增长说明,GpGraphics 造成了内
22、存泄漏,再利用.NET MemoryProfiler 捕捉内存 Heap 快照。显示方法中的 Bitmap、Graphics、LinearGradientBrush 三种类型出现了“Undisposed Instances”警告。这里,因为 Graphics 没有释放导致 Grahics 上引用的 Bitmap,以及 Bimap 上的Bitmapbmp = newBitmap(600, 600); Graphicsg = Graphics.FromImage(bmp); Brushbrush = newLinearGradientBrush (newPointF(0.0f, 0.0f), ne
23、wPointF(700.0f, 300.0f), Color.Blue, Color.Red);for (intj = 0; j 60; +j) for (inti = 0; i 60; +i)g.FillEllipse(brush, i * 10, j * 10, 10, 10);this.CreateGraphics().DrawImage(bmp, 0, 0);ApplicationContextComponentComponentDesignerBrushContainerContextCursorFileStreamDataSetFontIconImageMatrixTextureO
24、dbcDataReaderOleDBDataReaderPenRegexSocketStreamWriterTimerTooltipBitmapLinearGradientBrush 对象都没有被及时,造成内存泄漏。将代码:再运行AQTime 和.NET Memory Profiler,可以看到.NET Memory Profiler 的警告消除了,AQTime 显示 GpsGraphics 的 Live Count 一直是 1,不再会增加。由此得知,.NET中的Drawing托管对象使用也会造成内存泄漏,以上泄漏很容易被忽视,因为如果这种泄漏内存的增长量不大,在整个程序运行时显得微道,而不容
25、易察觉,另外因为Form作为继承了Idisable接口的控件容器,在其关闭时会自动调用其每个被对象的Dise方法,所以最后Form也会帮你回收的。Form的Dise方法如下:protectedoverridevoidDise(booldising)if(dising&(components!=null)/每个被对象的Dise方法 components.Disbase.Dise(dising);通常,好的编程要求程序员在使用完非托管资源的对象后应尽快不再使用的对象和资源来避免潜在的内存泄漏。这种包装对象的方法有 3 种:显式通过 Dise 方法()例如:font.Dise();隐式通过 Usin
26、g 语句()e();using(Graphicsg1 = this.CreateGraphics() using(Bitmapbmp = newBitmap(600,600) using(Graphicsg = Graphics.FromImage(bmp) using(Brushbrush = newLinearGradientBrush(newPoF(0.0f,0.0f), newPoF(700.0f,300.0f), Color.Blue,Color.Red) for (j = 0;j60;+j) for (i = 0;iNew Project.2).在 Setup 添加LuboView
27、.exe。3).选择 Profiler 为Resource Profiler。4).点击 Run 按钮,在弹出的对话框中点击Run。5).在 Event View 中可以查看到以下信息:EventThread IDTime-Project run selected, current profiler is Resource Profiler.15:18:30:843Product: AQtime; Version: 4.92.669.0非托管资源是指 CLR 不能控制或管理的部分,这些资源一般不存在与 Heap 中,与非托管资源不同,托管资源一般指被 CLR 控制的内存资源,这些资源可以由 C
28、LR 来控制,比如程序中的对象以及变量等,在.NET 中,CLR 提供一种独立的内存管理机制,不需要程序员在代码中显式的去释放使用到的内出资源,这种管理机制称为 GC(Garbage Collection),而对于非托管资源,CLR 只能跟踪非托管资源的生存期,而不能主动的去做 GC,这样就需要去显式或隐式的去释放这些资源,.NET 提供的 Fianlize 和 Dispose 方法分别就是隐式和显式释放只要的方法。-Work environment:Host name:-ZGDMOS:Windows XP Service Pack 2 5.1Build 2600Windows directo
29、ry: C:WINDOWS; System directory: C:WINDOWSSYSTEM32Current user: AdministratorNumber of prosors: 2Prosor:el(R) Pentium(R) D CPU 3.00GHz, Frequency:2992 MHz.Memory in use: 42%Total Physical Memory: 2,145,427,456 bytes;Available Physical Memory: 1,238,437,888bytes; Total Virtual Memory: 2,147,352,576by
30、tes; Available Virtual Memory: 1,806,143,488bytes; Virtual Memory In Use: 341,209,088 bytes.NETFramework ver: v2.0.50727-Run Mode: NormalHost Application:Parameters:Work Directory:从以上事件信息中可以看到AQTIME 在 RUN 之后,首先会获取如下一些信息:a).当前 AQTIME 的版本信息:Product:Aqtime;Ver:4.92.669.0b).系统工作环境:Host name:-ZGDMOS:Wind
31、ows XP Service Pack 2 5.1 Build 2600Windows directory: C:WindowsSystem directory:C:WindowsSYSTEM32Current user:AdministratorNumber of Prosor:2Prosor:er Pentinum CPU 3.00GHz,Frequency:2992MHz.Memory in use:42%Total Physical Memory:2,145,427,456 bytes;=2GBAvailable Physical Memory:1,238,437,888 bytes;
32、=1.15GBTotal Virtual Memory:2,147,352,576 bytes;=2GBAvailable Virtual Memory:1,806,143,488 bytes;=1.68GBPros create ID: 5592, Thread ID: 4216, Base address: 0 x00400000421615:18:31:000Module loaded: D:TestLuboViewDebugLuboView.exe; Base address: 0 x00400000 Size: 3039232 Ver: 421615:18:31:000
33、.NET Framework ver:v2.0.50727c).运行参数。因为没有在RUN-parameters 下设置相关内容,所以此处为空。但这里有何作用还有待于将来继续实践。d).创建进程,进程 ID 号为 5592,线程 ID 号为 4216,基址为 0 x00400000e).加载程序:D:TestLuboViewDebugLuboView.exe;基址:0 x00400000大小:3039232版本:f).加载动态库:C:Windowssystem32ntdll.dll基址:0 x7C920000大小:591360版本:5.1.2600.2180.加载其他动态库,如k
34、ernel32.dll,user32.dll,gdi32.dll6).Monitor 面板提示:当前的profiler 不支持Monitor 面板;Disassembler 面板和Editor 面板中会显示汇编代码和VC 源代码,前提配置了源代码文件的搜索路径。Details 面板,Call Graph 面板,Call Tree 面板 没有内容。7).LuboView加载曲线。8).结束 LuboView 进程,查看AQtime 的统计结果:a).资源文件:点击侧边栏Last Results 中Classes 项Virtual Memory in Use: 341,209,088 bytes=325.4MB从以上分析结果可以看出,一共建立了图标类 67 个,而有 57 个没有;表类创建了 376 个,有 29 个没有;DC 设备类有 400个创建,14 个没有;位图BitMap 创建了 236 个,9 个没有;PEN 类创建了 181 个,全部等等。并且在侧边栏的Last Results中有Errors 相关的,内容主要是句柄无效。这可能与之前杀LuboView 进程所造
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 职业学院学生晚出、晚归、不归管理办法
- 2025年度绿色生态园承建及景观装修合作协议3篇
- 2024年计件工作制职工聘用协议版B版
- 2025年度电商平台短信催收合作协议范本3篇
- 2024年版公司员工通勤巴士租赁协议版B版
- 2024年赡养老年人义务合同示例一
- 人教版小学六年级数学上册第二单元《位置与方向(二)》及练习五课件
- 中国特色社会主义理论与实践研究(湖大简答题)
- 学校传染病和突发公共卫生事件处理流程图
- 2024年检验类之临床医学检验技术(师)通关试题库(有答案)
- 部编版人教版五年级上册《道德与法治》全册教案-教学反思(新教材)
- 殡葬各领域知识点总结汇总
- 叉车维修检验原始记录
- Invoice商业发票模板
- 污废水处理设施运行管理课件
- 业务下单流程标准规范
- “家园”协力小班幼儿劳动教育的实践研究 论文
- 科学版二年级《游戏迎面接力跑》评课稿
- 信访事项复查申请书
- 巡检记录表巡检记录表
- 小学生家长教育焦虑调查问卷
评论
0/150
提交评论