内存分析利器purify简介

内存分析利器purify简介1 内存问题的原因及分类在 C C 程序中 有关内存使用的问题是最难发现和解决的 这些问题可能导致程序莫名其妙地停止 崩溃 或者不断消耗内存直至资源耗尽 由于 C C 语言本身的特质和历史原因 程序员使用内存需要注意的事项较多 而且语言本身也不提供类似 Java 的垃圾清理机制 编程人员使用一定的工具来查找和调试内存相关问题是十分必要的 总的说来 与内存有关的问题可以分成两类 内存访问错误和内

1.内存问题的原因及分类

在C/C++程序中,有关内存使用的问题是最难发现和解决的。这些问题可能导致程序莫名其妙地停止、崩溃,或者不断消耗内存直至资源耗尽。由于C/C++语言本身的特质和历史原因,程序员使用内存需要注意的事项较多,而且语言本身也不提供类似Java的垃圾清理机制。编程人员使用一定的工具来查找和调试内存相关问题是十分必要的。

总的说来,与内存有关的问题可以分成两类:内存访问错误和内存使用错误。内存访问错误包括错误地读取内存和错误地写内存。错误地读取内存可能让你的模块返回意想不到的结果,从而导致后续的模块运行异常。错误地写内存可能导致系统崩溃。内存使用方面的错误主要是指申请的内存没有正确释放,从而使程序运行逐渐减慢,直至停止。这方面的错误由于表现比较慢很难被人工察觉。程序也许运行了很久才会耗净资源,发生问题。

1.1 内存解剖

一个典型的C++内存布局如下图所示:

内存分析利器purify简介 

自底向上,内存中依次存放着只读的程序代码和数据,全局变量和静态变量,堆中的动态申请变量和堆栈中的自动变量。自动变量就是在函数内声明的局部变量。当函数被调用时,它们被压入栈;当函数返回时,它们就要被弹出堆栈。堆栈的使用基本上由系统控制,用户一般不会直接对其进行控制,所以堆栈的使用还是相对安全的。动态内存是一柄双刃剑:它可以提供程序员更灵活的内存使用方法,而且有些算法没有动态内存会很难实现;但是动态内存往往是内存问题存在的沃土。

1.2 内存访问错误

相对用户使用的语言,动态内存的申请一般由malloc/new来完成,释放由free/delete完成。基本的原则可以总结为:一对一,不混用。也就是说一个malloc必须对应一且唯一的free;new对应一且唯一的delete; malloc不能和delete, new不能和free对应。另外在C++中要注意delete和delete[]的区别。delete用来释放单元变量,delete[]用来释放数组等集聚变量。有关这方面的详细信息可以参考[C++Adv]。

我们可以将内存访问错误大致分成以下几类:数组越界读或写、访问未初始化内存、访问已经释放的内存和重复释放内存或释放非法内存。

下面的代码集中显示了上述问题的典型例子:

1   #include 
          
      
          
          
          
          
          
           
           
           
           
           
       
           
2   using namespace std;
3   int main(){
4      char* str1="four";
5      char* str2=new char[4];	//not enough space
6      char* str3=str2;
7      cout<
           
       
           
           
           
           
           
          
      
          
          
          
          
          

由以上的程序,我们可以看到:在第5行分配内存时,忽略了字符串终止符”/0″所占空间导致了第8行的数组越界写(Array Bounds Write)和第9行的数组越界读(Array Bounds Read); 在第7行,打印尚未赋值的str2将产生访问未初始化内存错误(Uninitialized Memory Read);在第11行使用已经释放的变量将导致释放内存读和写错误(Freed Memory Read and Freed Memory Write);最后由于str3和str2所指的是同一片内存,第12行又一次释放了已经被释放的空间 (Free Freed Memory)。

这个包含许多错误的程序可以编译连接,而且可以在很多平台上运行。但是这些错误就像定时炸弹,会在特殊配置下触发,造成不可预见的错误。这就是内存错误难以发现的一个主要原因。

1.3 内存使用错误

内存使用错误主要是指内存泄漏,也就是指申请的动态内存没有被正确地释放,或者是没有指针可以访问这些内存。这些小的被人遗忘的内存块占据了一定的地址空间。当系统压力增大时,这些越来越多的小块将最终导致系统内存耗尽。内存使用错误比内存访问错误更加难以发现。这主要有两点原因:第一,内存使用错误是”慢性病”,它的症状可能不会在少数、短时间的运行中体现;第二,内存使用错误是因为”不做为”(忘记释放内存)而不是”做错”造成的。这样由于忽略造成的错误在检查局部代码时很难发现,尤其是当系统相当复杂的时候。

2.Purify的原理及使用

IBM Rational PurifyPlus是一组程序运行时的分析软件。她包括了程序性能瓶颈分析软件Quantify, 程序覆盖面分析软件PureCoverage,和本文的主角:程序运行错误分析软件Purify。Purify可以发现程序运行时的内存访问,内存泄漏和其他难以发现的问题。

同时她也是市场上唯一支持多种平台的类似工具,并且可以和很多主流开发工具集成。Purify可以检查应用的每一个模块,甚至可以查出复杂的多线程或进程应用中的错误。另外她不仅可以检查C/C++,还可以对Java或.NET中的内存泄漏问题给出报告。

2.1 Purify的原理

程序运行时的分析可以采用多种方法。Purify使用了具有专利的目标代码插入技术(OCI:Object Code Insertion)。她在程序的目标代码中插入了特殊的指令用来检查内存的状态和使用情况。这样做的好处是不需要修改源代码,只需要重新编译就可以对程序进行分析。

对于所有程序中使用的动态内存,Purify将它们按照状态进行归类。这可以由下图来说明(来自[DEV205]):

内存分析利器purify简介 

参见本文中以上给出的代码,在程序第5行执行后,str2处于黄色状态。当在第7行进行读的时候,系统就会报告一个访问未初始化内存错误(Uninitialized Memory Read)。因为只有在绿色状态下,内存才可以被合法访问。

为了检查数据越界错误(ABR,ABW),Purify还在每个分配的内存前后插入了红色区域。这样一来,超过边界的访问指令必定落在非法区域,从而触发ABR或者ABW错误报告。这里需要指出一点。访问未初始化内存错误UMR在某些情况下其实是合法的操作,例如内存拷贝。所以在分析报告时可以把UMR放到最后,或者干脆从结果中滤除。

2.2 Purify的使用

这里简单介绍一下Purify在Windows和UNIX环境下的使用。

在Windows中,只要运行Purify,填入需要分析的程序及参数就可。Purify会自动插入检测代码并显示报告。报告的格式如下(来自[DEV205]):

内存分析利器purify简介 

蓝色的图标代表一些运行的信息,比如开始和结束等。黄色是Purify给出的警告。通常UMR会作为警告列出。红色则代表严重的错误。每一种相同的错误,尤其是在循环中的,会被集中在一起显示,并且标明发生的次数。由每个错误的详细信息,用户可以知道相应的内存地址和源代码的位置,并直接修改。另外用户还可以设置不同的滤过器,用来隐藏暂时不关心的消息。

在UNIX系统中,使用Purify需要重新编译程序。通常的做法是修改Makefile中的编译器变量。下面是用来编译本文中程序的Makefile:

        CC=purify gcc
all: pplusdemo
pplusdemo: pplusdemo.o
        $(CC) -o pplusdemo pplusdemo.o -lstdc++
pplusdemo.o: pplusdemo.cpp
        $(CC) -g -c -w pplusdemo.cpp
clean:
        -rm pplusdemo pplusdemo.o
        

首先运行Purify安装目录下的purifyplus_setup.sh来设置环境变量,然后运行make重新编译程序。需要指出的是,程序必须编译成调试版本。在gcc中,也就是必须使用”-g”选项。在重新编译的程序运行结束后,Purify会打印出一个分析报告。它的格式和含义与Windows平台大同小异。

下面是本文中的程序在Linux上Purify运行的结果:

      Purify instrumented ./pplusdemo (pid 30669)  
UMR: Uninitialized memory read:
  * This is occurring while in:
	strlen         [rtlib.o]
	std::basic_ostream< char,std::char_traits< char>> & std::operator
	<<
                    
      
                    
                    
                    
                    
                    
                     
                     
                     
                     
                     
       
                     >(std::basic_ostream< char,std::char_traits<
	char>> &, char const *) [libstdc++.so.5]
	main           [pplusdemo.cpp:7]
	__libc_start_main [libc.so.6]
	_start         [crt1.o]
  * Reading 1 byte from 0x80b45e0 in the heap.
  * Address 0x80b45e0 is at the beginning of a malloc'd block of 4 bytes.
  * This block was allocated from:
	malloc         [rtlib.o]
	operator new( unsigned) [libstdc++.so.5]
	operator new []( unsigned) [libstdc++.so.5]
	main           [pplusdemo.cpp:5]
	__libc_start_main [libc.so.6]
	_start         [crt1.o]
  Purify instrumented ./pplusdemo (pid 30669)  
ABW: Array bounds write:
  * This is occurring while in:
	strcpy         [rtlib.o]
	main           [pplusdemo.cpp:8]
	__libc_start_main [libc.so.6]
	_start         [crt1.o]
  * Writing 5 bytes to 0x80b45e0 in the heap (1 byte at 0x80b45e4 illegal).
  * Address 0x80b45e0 is at the beginning of a malloc'd block of 4 bytes.
  * This block was allocated from:
	malloc         [rtlib.o]
	operator new( unsigned) [libstdc++.so.5]
	operator new []( unsigned) [libstdc++.so.5]
	main           [pplusdemo.cpp:5]
	__libc_start_main [libc.so.6]
	_start         [crt1.o]
  Purify instrumented ./pplusdemo (pid 30669)  
ABR: Array bounds read:
  * This is occurring while in:
	strlen         [rtlib.o]
	std::basic_ostream< char,std::char_traits< char>> & std::operator
	<<
                     
       
                     
                     
                     
                     
                     
                       >(std::basic_ostream< char,std::char_traits< char>> &, char const *) [libstdc++.so.5] main [pplusdemo.cpp:9] __libc_start_main [libc.so.6] _start [crt1.o] * Reading 5 bytes from 0x80b45e0 in the heap (1 byte at 0x80b45e4 illegal). * Address 0x80b45e0 is at the beginning of a malloc'd block of 4 bytes. * This block was allocated from: malloc [rtlib.o] operator new( unsigned) [libstdc++.so.5] operator new []( unsigned) [libstdc++.so.5] main [pplusdemo.cpp:5] __libc_start_main [libc.so.6] _start [crt1.o] Purify instrumented ./pplusdemo (pid 30669) FMM: Freeing mismatched memory: * This is occurring while in: operator delete( void *) [rtlib.o] main [pplusdemo.cpp:10] __libc_start_main [libc.so.6] _start [crt1.o] * Attempting to free block at 0x80b45e0 in the heap. * Address 0x80b45e0 is at the beginning of a malloc'd block of 4 bytes. * This block was allocated from: malloc [rtlib.o] operator new( unsigned) [libstdc++.so.5] operator new []( unsigned) [libstdc++.so.5] main [pplusdemo.cpp:5] __libc_start_main [libc.so.6] _start [crt1.o] * This block of memory was obtained using an allocation routine which is not compatible with the routine by which it is being freed. Purify instrumented ./pplusdemo (pid 30669) FMR: Free memory read: * This is occurring while in: main [pplusdemo.cpp:11] __libc_start_main [libc.so.6] _start [crt1.o] * Reading 1 byte from 0x80b45e0 in the heap. * Address 0x80b45e0 is at the beginning of a freed block of 4 bytes. * This block was allocated from: malloc [rtlib.o] operator new( unsigned) [libstdc++.so.5] operator new []( unsigned) [libstdc++.so.5] main [pplusdemo.cpp:5] __libc_start_main [libc.so.6] _start [crt1.o] * There have been 0 frees since this block was freed from: free [rtlib.o] _ZdLpV [libstdc++.so.5] main [pplusdemo.cpp:10] __libc_start_main [libc.so.6] _start [crt1.o] Purify instrumented ./pplusdemo (pid 30669) FMW: Free memory write: * This is occurring while in: main [pplusdemo.cpp:11] __libc_start_main [libc.so.6] _start [crt1.o] * Writing 1 byte to 0x80b45e0 in the heap. * Address 0x80b45e0 is at the beginning of a freed block of 4 bytes. * This block was allocated from: malloc [rtlib.o] operator new( unsigned) [libstdc++.so.5] operator new []( unsigned) [libstdc++.so.5] main [pplusdemo.cpp:5] __libc_start_main [libc.so.6] _start [crt1.o] * There have been 0 frees since this block was freed from: free [rtlib.o] _ZdLpV [libstdc++.so.5] main [pplusdemo.cpp:10] __libc_start_main [libc.so.6] _start [crt1.o] Purify instrumented ./pplusdemo (pid 30669) FUM: Freeing unallocated memory: * This is occurring while in: free [rtlib.o] _ZdLpV [libstdc++.so.5] main [pplusdemo.cpp:12] __libc_start_main [libc.so.6] _start [crt1.o] * Attempting to free block at 0x80b45e0 already freed. * This block was allocated from: malloc [rtlib.o] operator new( unsigned) [libstdc++.so.5] operator new []( unsigned) [libstdc++.so.5] main [pplusdemo.cpp:5] __libc_start_main [libc.so.6] _start [crt1.o] * There have been 1 frees since this block was freed from: free [rtlib.o] _ZdLpV [libstdc++.so.5] main [pplusdemo.cpp:10] __libc_start_main [libc.so.6] _start [crt1.o] Purify instrumented ./pplusdemo (pid 30669) Current file descriptors in use: 5 FIU: file descriptor 0: 
                      
                        FIU: file descriptor 1: 
                       
                         FIU: file descriptor 2: 
                        
                          FIU: file descriptor 26: 
                         
                           FIU: file descriptor 27: 
                          
                            Purify instrumented ./pplusdemo (pid 30669) Purify: Searching for all memory leaks... Memory leaked: 0 bytes (0%); potentially leaked: 0 bytes (0%) Purify Heap Analysis (combining suppressed and unsuppressed blocks) Blocks Bytes Leaked 0 0 Potentially Leaked 0 0 In-Use 0 0 ---------------------------------------- Total Allocated 0 0 Purify instrumented ./pplusdemo (pid 30669) * Program exited with status code 0. * 7 access errors, 7 total occurrences. * 0 bytes leaked. * 0 bytes potentially leaked. * Basic memory usage (including Purify overhead): 290012 code 152928 data/bss 6816 heap (peak use) 7800 stack 
                           
                          
                         
                        
                       
                     
                    
      
                    
                    
                    
                    
                    

我们对照程序可以发现Purify查出了程序中所有的错误。对于每个错误,她不但给出了源代码的位置还指出这些内存最初分配的源代码位置。这对于查找问题提供了很大帮助。对于程序12行的解释,Purify将其认为是不匹配的内存释放(FMM: Freeing mismatched memory),因为她认为这样的释放方式不符合严格的规定。

Purify在其报告和文档中使用了很多的缩写,在此一并列出,以便读者在使用时参考(来自[Purify]):

内存分析利器purify简介 

2.3 Purify的一些特性

这里简单介绍一下Purify提供的几个特性。有关这些特性的详细信息,请查阅文档[Purify]。

  • 观察点(Watchpoint):通过在程序或者调试器中调用Purify 提供的观察点函数,Purify可以报告有关被观察对象的读写或其他操作。
  • 与Rational其他产品的集成:在Puify的用户界面中可以方便地进入ClearCase和ClearQuest。Purify还可以和PureCoverage同时使用,对程序进行分析。
  • Purify的定制:无论是Purify报告中的消息,还是界面中的元素,都可以进行一定程度的定制。另外通过修改配置文件和调用Purify API,用户还可以自动记录运行日志,发送电子邮件等。
  • Purify提供的API:为了更好地把Purify融合到自动化测试的体系中,Purify提供了一系列的公开函数。用户完全可以通过脚本的方式自动运行,记录,和分析Purify。

3.总结

当使用C/C++进行开发时,采用良好的一致的编程规范是防止内存问题第一道也是最重要的措施。在此前提下,IBM Rational Purify作为一种运行时分析软件可以很好地帮助您发现忽略的内存问题,或成为软件自动测试中的一个重要组成部分。

4.参考资料

 

 

 

 

补充阅读:

http://blog.csdn.net/haoel/archive/2003/12/16/2904.aspx





























版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/221155.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 下午6:48
下一篇 2026年3月17日 下午6:48


相关推荐

  • partprobe分区报错

    partprobe分区报错环境 centos6 9vmware 虚拟机场景 对磁盘进行在线扩容 扩容后 在线扫描 扫描过后进行 LVM 扩展 分区后 通过 partprobe dev sda 扫描 出现以下错误 分区报错 shell gt partprobe dev sdaWarning WARNING thekernelfai readtheparti

    2026年3月26日
    2
  • 产品分享:Qt视频播放器(不依赖系统编解码),当前版本v1.1.2

    产品分享:Qt视频播放器(不依赖系统编解码),当前版本v1.1.2欢迎技术交流和帮助,提供IT相关服务,索要源码请联系博主QQ:21497936,若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936本文章博客地址:https://blog.csdn.net/qq21497936/article/details/100180789目录前言Qt自带播放器框架存在问题Demov1.1…

    2022年5月8日
    48
  • 数据中心的变化需要更多有效冷却方式[通俗易懂]

    数据中心的变化需要更多有效冷却方式[通俗易懂]数据中心的变化需要更多有效冷却方式

    2022年4月20日
    41
  • hdu 3832 Earth Hour (最短路变形)

    hdu 3832 Earth Hour (最短路变形)

    2022年1月22日
    47
  • Java开发不得不会!郑州java外包公司有哪些

    Java开发不得不会!郑州java外包公司有哪些拼多多三面惨败,java中间件、数据库与spring框架,答不上…面试开火箭,工作拧螺丝…月初有个朋友面试拼多多,一面自信满满过了,结果三面却惨败…昨天朋友约我出来讨论问题在哪里,三面就答不上了(卡壳很严重呐)。听完整个面试经历之后,我总结了一下,这次拼多多一二三面其实综合总结起来就问了五个方面的东西,分别是自我介绍+项目、java基础、中间件、数据库和spring框架,另外还问了道算法题。接下来就跟大家一起来看看拼多多的面经,看看你是否能轻松应对面试官的炮轰?技术焦虑现在的技术圈子很火热,任何技术

    2022年5月11日
    69
  • 倒立摆模型分析_倒立摆系统建模方法

    倒立摆模型分析_倒立摆系统建模方法模型对小车的水平受力分析Mx¨=F−bx˙−NM\ddot{x}=F-b\dot{x}-NMx¨=F−bx˙−Nx¨\ddot{x}x¨代表对运动距离的二阶微分,即小车在外力作用下的加速度。FFF是外部施加给系统的外力。x˙\dot{x}x˙代表小车当前的运动速度,小车所受到的摩擦力为摩擦系数与小车运动速度之积,即f=bx˙f=b\dot{x}f=bx˙。NNN为倒单摆作用给小车水平方向的力。对倒单摆的受力分析摆杆做平面运动,可以分解为质心的平动和绕质心的转动。N=mx¨−(mlψ

    2022年8月18日
    9

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号