解决Solaris应用程序开发内存泄漏问题 (1)

原创 2006年06月07日 13:33:00
作者: 李凌云,张一峰(laoeyu)

概述

内存泄漏是应用软件开发过程中经常会遇到的问题,应用长期内存泄漏会占用大量操作系统内存资源,直接导致应用程序运行不稳定,严重时甚至还会影响到操作系统的正常运行。为了找到应用程序内存泄漏点,许多开发人员不得不在上千行乃至几十万行源程序中加入更多的调试代码,试图从调试信息中找到内存泄漏的根源,但通常来讲这种方法是事倍功半的。幸运的是,Solaris平台提供了好几个实用的工具,能够辅助开发人员对内存泄漏根源进行定位。笔者参考了 Sun公司官方网站上相关的英文技术文档,并认为这些对于我们中国广大的Sun技术爱好者有很好的指导作用。笔者经过消化整理后写下此文希望与大家共享。在下面的章节 中将初步讲述如何在Solaris 10操作系统下利用这些工具查找用户程序的内存泄漏点。文章各节标题如下:

1. 概述
2. 内存泄漏及危害
3. dbx
4. libumem
5. DTrace
6. libgc
7.
总结
8.
参考资料


内存泄漏及危害

如果要给内存泄漏下个定义的话,它应该属于软件程序设计的一种缺陷,该缺陷直接导致了程序在运行过程中无法释放不再需要的内存空间,从而造成内存资源浪费。具体来说,当用户程序在运行过程中需要动态获得内存时,操作系统总是从堆(heap)上分配相应的空间给应用,分配的结果是将该堆内存的起始地址通过指针返回给应用。正常情况下,应用使用完这块内存后,应通过系统调用主动通知操作系统回收这些堆内存以便重用。但是,如果由于设计缺陷导致在某些情况下程序没有主动地通知到操作系统,而后应用又失去了对这块内存的引用时,则该堆内存块将成为既不受程序控制,又不能被系统回收重用的“孤儿”内存,这便是我们所指的内存泄漏。

造成内存泄漏的设计缺陷多种多样,下面例举了部分典型的内存泄漏设计缺陷,它们都是开发人员经常会犯的毛病。

一个原先运行正常的系统由于安装运行了用户应用程序后经常发生以下任意症状时,则应用程序有可能存在内存泄漏问题:

  • 没有特殊原因,应用程序经长时间运行后所占虚拟内存总量仍在持续性地不断增长(即使增长过程十分缓慢)。通过使用prstat可以查询进程内存使用情况。

  • swap设备配置和运行正常,且没有大文件占用/tmp目录的情况下,经常有进程报告“Out of Memory”错误。通过使用swap -s命令可以查询swap空间使用情况,以及使用df -k查询/tmp的使用情况。

一个有内存泄漏问题的应用程序经过长时间运行后,通常会逐渐占用大量操作系统内存。操作系统会因内存短缺而造成整体性能下降,严重时可以造成系统中其他真正需要内存的进程因得不到内存空间而无法正常运行,操作系统也会由于内存耗尽而变得不稳定。在下面的章节中,我们将介绍在Solaris 10平台上有哪些工具帮助我们分析内存泄漏问题。

dbx

Sun StudioSun公司推出的面向CC++Fortran语言编程的开发环境,目前最新版本是Sun Studio 11。它包括一个非常友好而专业的GUI集成开发环境,以及像dbxPerformance Analyzer等优秀的辅助工具。其中,dbx工具除了可以帮助开发人员进行源代码级别的跟踪调试以外,它还可以帮助开发人员查找和定位应用程序中内存泄漏的问题。令人兴奋的是,Sun Studio 11不像以前各版本那样需要购买License,它是免费下载和使用的(包括商用),有兴趣的话可以到http://developers.sun.com/prodtech/cc/downloads/index.jsp下载。

Runtime Checking

dbx中提供内存泄漏检查功能的模块被称为RTC,即Runtime Checking。它除了提供内存泄漏检查功能外,还可以进行内存访问检查和使用检查,对于发现程序中内存越界访问或者变量未初始化就访问等编程问题很有帮助。缺省情况下,dbx不启用内存泄漏检查功能,用户可以通过check -leaks命令启用它。check -leaks是一个反复切换开关,在已经启用内存泄漏检查功能的情况下再使用该命令可以关闭这个功能。使用内存泄漏检查功能不需要对程序进行重编译,它也支持在优化过的目标代码中查找内存泄漏。

使用RTC模块有一些前提要求,比如:

  • 程序必须是由Sun提供的编译器所编译生成的;

  • 程序使用动态库方式链接libc库;

  • 内存是通过libc库的malloc()free()realloc()或其他基于这些调用的函数进行申请和管理的;

  • 程序不能被完全strip,即符号表必须存在。strip -x命令仍可以接受。

RTC还有部分限制,比如:

  • 只能在Solaris操作系统上使用;

  • 在基于非UltraSPARC芯片的主机系统上使用时,程序的text段和数据data段不能超过8MB空间。

RTC对于内存泄漏分三种情况:

  • Memory Leak(mel),即进程中不存在任何一个指针指向某内存块,则该内存块为真正的内存泄漏块。

  • Address in Block(aib),即进程中不存在任何一个指针指向某内存块的启始位置,却存在指向该内存块中间某位置的指针。这是一个可疑的内存泄漏,即它很可能会演变为Memroy Leak,但也不排除程序设计者为了某种需要故意设计成这样的。
  • Address in Register(air),即进程代码段及数据段中没有任何一个指针指向该内存块,但在至少一个寄存器中存在相关的指针。这是一个可疑的内存泄漏。如果程序在编译时使用了优化选项,比如-O等,则编译器有可能只将内存指针保留于寄存器中,否则这会演变为真正的内存泄漏。
dbx在报告内存泄漏时会区分上述三种情况,对于可能的内存泄漏,开发人员需要自行判断是否为真正的内存泄漏。

用dbx查内存泄漏

使用dbx检查内存泄漏是所有工具中最方便的。如果程序在编译时使用了-g选项,dbx可以很方便地将内存泄漏点定位到源程序代码行。使用dbx检查内存泄漏的典型过程如下:

1. 使用dbx启动被跟踪的程序。

2. 用check -leaks打开内存泄漏检查开关。

3. 运行程序直至结束。当程序运行结束时,dbx会给出类似以下的内存泄漏报告。


例子中报告了两个内存泄漏,分别为从main()过程调用到memory_leak()过程时有1次32字节的内存泄漏,以及从main()过程调用到address_in_register()过程时有1 次11字节的内存泄漏。为了得到具体的源代码行号,可以在运行程序前使用以下的命令将内存泄漏报告模式改为verbose,然后重新运行程序。


例子中报告了内存泄漏点分别在源代码第8行和第35行。

如果要检查一个守护进程类型的服务程序是否发生内存泄漏,上述方法就不适用了,这是因为守护进程永远不会运行结束。对此,dbx提供了一个showleaks的命令可以让开发人员在任何时候查看进程内存泄漏情况。另外,守护进程一般会多次进行fork(),所以也不适合采用dbx直接进行启动。因此,对于守护进程类程序,开发人员可以通过以下方法启动,然后dbx动态挂接到已运行的进程上再进行内存泄漏检查。

1. 设定环境变量,预装librtc.so

缺省情况下在程序启动时librtc.so不会预装入系统。这意味着即使后来dbx动态挂接上该进程后,也无法使用RTC功能。但开发人员可以通过设定LD_AUDIT环境变量,指定应用程序启动时系统预装入librtc.so。方法是:对于32位应用,将LD_AUDIT指向<Sun Studio 11安装目录>/lib/下的rtcaudit.so,对于SPARC 64位应用,须指向lib/v9下的rtcaudit.so,对于AMD 64位应用,则为lib/amd64/下rtcaudit.so

2. 启动守护程序,并得到进程号。注意,启动应用后应及时使用unset命令去除LD_AUDIT设置。后继命令不应使用LD_AUDIT

3. 令dbx动态挂接上守护进程,关闭同步跟踪,并打开内存泄漏检查。可根据需要在程序合适的位置设好断点,然后继续执行程序或单步跟踪执行程序。在必要的时候利用showleaks检查内存泄漏情况。

其中,showleaks命令缺省只报告自上次报告内存泄漏后新发现的内存泄漏。如果使用-a选项,则showleaks报告所有内存泄漏。-v选项是指verbose模式,可以给出更详细的报告。跟踪完成后,可使用quit命令退出dbx

libumem

libumem的由来是原自SunOS 5.4中的Kernel Slab Allocator。为了加速系统虚拟内存操作,Sun的工程师发明了Kernel Slab Allocator,后来也被推广到Linux操作系统。Kernel Slab Allocator通过一种object cacheing技术策略来实现高效内存的处理。在实际使用中,这种Kernel Slab Allocator内存分配器被证明非常高效,在多颗CPU上,扩展性(Scability)表现亦极佳。由于Kernel Slab Allocator是工作在kernel状态,所以相应地产生了一个在用户空间工作的内存分配器: libumem。自Solaris 9update3)开始,Solaris就自带这个全新的内存分配器: libumem

libumem不仅能够优化程序的内存分配,而且还提供内存分配调试,记录功能,配合mdb工具我们可以轻松观察程序内存的分配情况和内存泄漏。在使用libumem检测内存泄漏的问题的之前,我们必须了解一些在调试中libumem提供给我们信息的内存结构。

libumem工作内存结构

libumem也是使用Slab概念。SlabSlab Allocator中一个基本内存单元:Slab是代表一个或者多个虚拟内存中的页(Page),它通常会被分割成为多个大小等同的Chunks,被成为BufferBuffer含有用户所使用的数据,还会有一些额外的信息,不过这个取决环境变量的设置。这些额外的信息对我们调试,检测内存泄漏非常有用。下面就是Buffer的一个基本结构:

Buffer结构中第一个sectionMetadata,主要提供内存分配的长度信息,我们这里不使用,在32位程序应用中它是8个字节。Metadata后面是存储用户数据的User data Section。接着是Redzone部分,Redzone也是8个字节。最后是Debug Metadata也是8个字节。其中前四个字节代表一个指针,指向一个umem_bufctl_audit结构,这个结构记录着内存分配时候的堆栈。该结构的定义可以在/usr/include/umem_impl.h找到。后面四个字节是校验位,可以用来和前面字节一起来判断这个buffer有没有被破坏。

libumem使用方法

1. 预加载(Preload) libumem

如果在程序中需要调试,寻找内存泄漏,需要预先加载(Preload) libumem,并且设置上环境变量:UMEM_DEBUG=defaultUMEM_LOGGING=transactionLD_PRELOAD=libumem.so.1

csh中设置的例子

bash中设置的例子

2. 运行程序,在程序运行时使用gcore命令对目标程序的进程生成core文件。

3. 使用mdb命令倒入core文件。

  • ::umem_status命令查看libumem的日志功能是否打开。

  • ::findleaks命令查看是否有内存泄漏。

  • 内存泄漏地址$<bufctl_audit,该命令会将该地址的内容以umem_bufctl_audit的结构,并且会显示内存泄漏的时候的用户堆栈。

  • ::umalog命令查看每次内存分配的时间,地址,堆栈。

  • 内存地址::umem_verify可以查看内存是否被破坏,比如内存的越界操作。

  • 内存地址::umem_log可以按CPU,线程打印出内存分配记录。

解决Solaris应用程序开发内存泄漏问题

内存泄漏是应用软件开发过程中经常会遇到的问题,应用长期内存泄漏会占用大量操作系统内存资源,直接导致应用程序运行不稳定,严重时甚至还会影响到操作系统的正常运行。为了找到应用程序内存泄漏点,许多开发人员不...
  • undead
  • undead
  • 2011年05月26日 10:44
  • 704

解决Solaris应用程序开发内存泄漏问题 (2)

作者:李凌云, 张一峰(laoeyu)DTraceDTrace是一个动态监测工具,它是在Solaris 10系统中Sun公司推出 的一个全新工具。DTrace这个工具是一个内嵌在Solaris系统中的...
  • laoeyu
  • laoeyu
  • 2006年06月08日 21:38
  • 4059

解决内存泄露问题的改良版MVP模式

解决内存泄露问题的改良版MVP模式,防止Presenter一直持有Activity对象,使得Activity不能被回收,由此发生内存泄露...
  • eric_niezhangyu
  • eric_niezhangyu
  • 2016年09月07日 00:24
  • 1891

performSelectorInBackground 导致内存泄露的解决方法

程序中起了一个线程来调用某updataData的方法 [xxAdManager performSelectorInBackground: @selector(up...
  • MAZHEN1986
  • MAZHEN1986
  • 2012年04月09日 09:33
  • 831

经典内存泄漏及其解决方案

原文:http://www.cocoachina.com/ios/20160411/15892.html点击打开链接 4 经典内存泄漏及其解决方案 虽然ARC好处多多,然而也并无法...
  • qimuya
  • qimuya
  • 2016年04月11日 09:20
  • 2516

C++内存泄漏及解决方法

1.首先说到c++内存泄漏时要知道它的含义? 内存泄漏(memory leak)是指由于疏忽或错误造成了程序未能释放掉不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存...
  • Clever_Pig
  • Clever_Pig
  • 2017年07月13日 13:17
  • 908

Java语言中是否存在内存泄漏的问题

Java语言中是否存在内存泄漏的问题
  • jsqfengbao
  • jsqfengbao
  • 2015年03月31日 22:26
  • 982

C++智能指针解决内存泄漏问题

在C++开发中,好多程序员经常会被内存泄漏问题困扰,使用智能指针就可以减少内存泄漏。智能指针可以自动删除分配的内存。智能指针和普通指针类似,只是不需要手动释放指针,而是通过智能指针自己管理内存的释放。...
  • fanyun_01
  • fanyun_01
  • 2017年11月26日 21:56
  • 162

如何解决Python2的内存泄漏问题

python进程内存撑大了下不去,只能手动重启释放内存,这么坑爹的事情你遇到过吗?哪家内存分配器强?结果一目了然。线上进程目前都是使用原生的Python内存分配器(pymalloc),在正常的情况下表...
  • xiejueheng
  • xiejueheng
  • 2015年05月17日 22:08
  • 2062

Unity游戏引擎游戏开发时遇到内存泄漏问题怎么办? 自研发的Unity游戏老是卡顿、闪退,有什么办法解决吗?

文章链接: https://www.zhihu.com/question/49382993
  • qq_35202475
  • qq_35202475
  • 2017年12月09日 11:32
  • 143
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:解决Solaris应用程序开发内存泄漏问题 (1)
举报原因:
原因补充:

(最多只允许输入30个字)