如何ios检查内存泄露露问题

检测内存泄漏 - Fangzhen - 博客园
随笔 - 367, 文章 - 174, 评论 - 112, 引用 - 0
网上搜索了一下,发现检测内存泄漏的工具还是很多的。下面是从网上找到的一些材料,主要是在linux系统中内存泄漏的检测方法。
(1)什么是内存内存泄漏?  在此,谈论的是程序设计中内存泄漏和错误的问题,不过,并不是所有的程序都有这一问题。首先,泄漏等一些内存方面的问题在有的程序语言中是不容易发生的。这些程序语言一般都认为内存管理太重要了,所以不能由程序员来处理,最好还是由程序语言设计者来处理这些问题,这样的语言有Perl、Java等等。     然而,在一些语言(最典型的就是C和C++)中,程序语言的设计者也认为内存管理太重要,但必需由开发人员自己来处理。内存泄漏指的是程序员动态分配了内存,但是在使用完成后却忘了将其释放。除了内存泄漏以外,在开发人员自己管理内存的开发中,缓冲溢出、悬摆指针等其它一些内存的问题也时有发生。      (2) 问题缘何产生  为了让程序能够处理在编译时无法预知的数据占用内存的大小,所以程序必需要从操作系统实时地申请内存,这就是所谓的动态内存。这时候,就会出现程序申请到内存块并且使用完成后,没有将其归还给操作系统的错误。更糟的情况是所获取的内存块的地址丢失,从而系统无法继续识别、定位该内存块。还有其它的问题,比如试图访问已经释放的指针(悬摆指针),再如访问已经被使用了的内存(内存溢出)的问题。 (3)后果不容忽视    对于那些不常驻内存的程序来说,由于执行过程很短,所以即使有漏洞可能也不会导致特别严重的后果。不过对于一些常驻内存的程序(比如Web服务器Apache)来说,如果出现这样的问题,后果将非常严重。因为有问题的程序会不断地向系统申请内存,并且不释放内存,最终可能导致系统内存耗尽而导致系统崩溃。此外,存在内存泄漏问题的程序除了会占用更多的内存外,还会使程序的性能急剧下降。对于服务器而言,如果出现这种情况,即使系统不崩溃,也会严重影响使用。   悬摆指针会导致一些潜在的隐患,并且这些隐患不容易暴发。它非常不明显,因此很难被发现。在这三种存在的问题形式中,缓冲溢出可能是最危险的。事实上,它可能会导致很多安全性方面的问题(一个安全的程序包含很多要素,但是最重要的莫过于小心使用内存)。正如上面所述,有时也会发生同一内存块被多次返还给系统的问题,这显然也是程序设计上的错误。一个程序员非常希望知道在程序运行的过程中,使用内存的情况,从而能够发现并且修正问题。 (4)如何处理      现在已经有了一些实时监测内存问题的技术。内存泄漏问题可以通过定时地终止和重启有问题的程序来发现和解决。在比较新的Linux内核版本中,有一种名为OOM(Out Of Memory )杀手的算法,它可以在必要时选择执行Killed等程序。悬摆指针可以通过定期对所有已经返还给系统的内存置零来解决。解决内存溢出问题的方法则多种多样。     事实上,在程序运行时来解决这些问题,显然要麻烦得多,所以我们希望能够在开发程序时就发现并解决这些问题。下面介绍一些可用的自由软件。
&垃圾回收器(GC)MemprofValgrind简介在GCC工具包中,有一个"垃圾回收器(GC)",它可以轻松检测并且修正很多的内存问题。目前该项目由HP的Hans-J.Boehm负责。Memprof是一个非常具有吸引力且非常易于使用的软件,它由Red Hat的Owen Talyor创立。这个工具是用于GNOME前端的Boehm-Demers-Weiser垃圾回收器。Valgrind是一个致力于解决所有内存问题的程序,而内存泄漏只不过是其中的问题之一而已。该工具的开发人员是Julian Seward(以Bzip2和Cacheprof而闻名)。该工具宣称自己"是专门致力于解决x86 Linux中开放源代码的内存问题",事实上,它的确做到了自己的宣言。此外,它还可以描述CPU缓存的使用情况,不过这一功能并不常用。使用的技术GC使用的是名为Boehm-Demers-Weiser的可以持续跟踪内存定位的技术。它的算法通过使用标准的内存定位函数来实现。程序使用这些函数进行编译,然后执行,算法就会分析程序的操作。该算法非常著名并且比较容易理解,不会导致问题或者对程序有任何干扰。就其核心技术来说,Memprof和上面提到的GC没有什么本质的不同。不过在实现这一功能时,它是从程序中捕获所有的内存请示并且实时将其重定位到垃圾回收器。在这个程序中使用的技术非常复杂,不过其文档非常丰富和完整(http://developer.kde.org/~sewardj/docs/techdocs.html)。程序分配的每一字节的内存都被一个有九位的状况字跟踪,其目的是用于识别其意图。这种做法大大加重了系统的负担。性能该工具有很好的性能,故可以有效提高程序效率。其代码非常少并且可以直接在GCC中使用。该工具的性能非常不错,其GUI设计得也不错。这个工具直接就可以执行,并且其工作起来无需对源代码进行任何修改。在程序执行时,这个工具会以图形化的方式显示内存的使用情况,以帮助你了解程序运行过程中内存的申请情况这个工具是我们这儿介绍的三款中性能最差的一个,原因是显而易见的。该工具提供的信息细节是三个工具中最丰富的,因而速度也是最慢的。除了一些常见的问题外,该工具还可以发现内存其它的一些问题,甚至一些POSIX线程方面的问题。缓冲的信息对于大部分程序来说似乎没有必要,不过它是一个查看程序性能的很好方式。对于Valgrind来说,值得一提的就是其开发速度非常快,其开发社团也非常活跃。事实上,在Valgrind的主页上作者甚至有一句话:"如果你在使用Valgrind过程中有任何问题,请不要介意,给我发邮件吧"。缺点该工具没有界面,使用起来比较困难,所以要想掌握它还是要花一些工夫的。一些现有的程序很有可能无法使用这个编辑器进行配置。此外,为了让所有的调用能被捕获,所有的内存调用(比如malloc()和free())都必须要使用由GC提供的相应函数来代替。我们也可以使用宏来完成这一工作,但还是觉得不够灵活。该工具目前只能运行于x86和PPC体系结构之上的Linux系统之中。如果你需要用于其它的平台,应该想想使用其它的工具。该工具不是GTK应用程序,所以需要一个完整的GNOME环境。这样就使得其不能灵活用于所有的地方。此外,该工具的开发工作进展得也比较缓慢(现在是0.4.1版)。不过,该工具是专门用于x86的。其界面是纯命令行方式,但是其可用性非常好。该工具可以直接在二进制下运行,所以在使用时并不需要对其进行重新编译。不过要熟练掌握它,还是需要使用者进行一番努力的。此外,虽然该工具曾经使用于Mozilla、OpenOffice等一些大的线程程序,但该工具对线程的支持并不完善。我想如果该工具要是有一个GUI界面,将会赢得更多人的青睐。 结论如果你希望能够有跨平台(体系结构、操作系统)的解决方案,那么就是它了。如果你喜欢GUI工具并且不介意只能用于Linux以及GNOME之下,该工具应该可以说是非常不错。如果你使用x86,对自己的代码非常了解并且不介意使用命令行方式,那么这个程序将是你的至爱。 
& 结论:另外还有一些其他的工具,可在下面站点中找到很多检测内存错误的工具:。此外,还有一些商业工具,比如Purify、Geodesic等。在此就不详细介绍。教你如何实现内存泄漏检查
教你如何实现内存泄漏检查
  关于内存泄漏的检查网上有很多的例子和代码,其基本的方法都是用宏,替换掉内存分配以及释放的函数。但是现在网上很多的例子中没有一个是适合我们公司的需求的。
  具体的对内存泄漏检查有如下要求:
  1. 内存泄漏检查的代码尽可能少的占用CPU及内存
  2. 尽可能的不影响原程序
  因为,我们的服务器程序有泄漏而且是特殊情况下会泄漏,平时很难模拟出来。
  对于这种情况下的内存泄漏我以前的做法如下:
  1. 用写文件的方法记录所有的内存分配以及释放的操作
  2. 再写一个工具去分析所有的记录,从中找出泄漏的代码
  这样做需要大量的硬盘空间,不过,这个无所谓了现在硬盘很便宜!
  不过需要考虑到服务器程序当中包含了exe以及多个dll,为了通用,内存泄漏检查分为下面几个部分:
  1. IMemLeak.h IMemLeak.cpp 加入每一个模块当中
  2. MemLeakLog.dll 统一记录所有的内存操作,将其记录到文件当中
  3. MemCheckTool.exe 分析工具
  //IMemLeak.h &  #ifndef&_YG_MEMDBG_H_ &  #define_YG_MEMDBG_H_ &  #include&&cstdlib&  //Redefines &  #definemalloc(size)&mallocb(size,&__FILE__,&__LINE__) &  #definefree(memblock)&freeb(memblock,&__FILE__,&__LINE__) &  #definerealloc(memblock,&size)&reallocb(memblock,&size,&__FILE__,&__LINE__) &  #definecalloc(num,&size)&callocb(num,&size,&__FILE__,&__LINE__) &  //Redefined&functions &  void*&mallocb(size_t&size,&constchar*pszFile,&intnLine); &  voidfreeb(void*memblock,&constchar*pszFile,&intnLine); &  void*&reallocb(void*memblock,&size_t&size,&constchar*pszFile,&intnLine); &  void*&callocb(size_t&num,&size_t&size,&constchar*pszFile,&intnLine); &  //For&C++ &  void*&operatornew(size_t&size,&constchar*pszFile,&intnLine); &  void*&operatornew[](size_t&size,&constchar*pszFile,&intnLine); &  voidoperatordelete(void*pvMem)&throw(); &  voidoperatordelete[](void*pvMem)&throw(); &  voidpre_delete(constchar*pszFile,&intnLine); &  //Redefine&new&and&delete &  #definenewnew(__FILE__,&__LINE__) &  #definedelete&pre_delete(__FILE__,&__LINE__),delete &  #endif &  //IMemLeak.cpp &  #include&&stdio.h&  #include&&tchar.h&  #include&&stdlib.h&  #include&&malloc.h&  #include&&Windows.h&  #include&&cstdlib&  enumEOperateType &  { &  Type_Malloc, &  Type_Calloc, &  Type_Realloc, &  Type_New, &  Type_New_Array, &  Type_Free, &  Type_Delete, &  Type_Delete_Array &  }; &  typedef&void(__stdcall&*&pFun_MemLeakLog)(LPCSTR&PLog); &  pFun_MemLeakLog&MemLeakLog&=&NULL; &  voidCheckMemLeakLogDLL() &  { &  if(MemLeakLog&==&NULL) &  { &  HINSTANCE&hinstLib&=&LoadLibrary(_T(&MemLeakLog.dll&)); &  if(hinstLib&!=&NULL) &  { &  MemLeakLog&=&(pFun_MemLeakLog)GetProcAddress(hinstLib,&&MemLeakLog&); &  } &  } &  } &  voidLog(EOperateType&type,&void*&pmem,&size_t&size,&intnLine,&constchar*&pszFile) &  { &  CheckMemLeakLogDLL(); &  chartemp[1024]; &  if(MemLeakLog&!=&NULL) &  { &  memset(temp,&0,&1024); &  sprintf_s(temp,&1024,&&%d-%p-%d-%d&[%s]&&,&type,&pmem,&size,&nLine,&pszFile); &  MemLeakLog(temp); &  } &  } &  void*&mallocb(size_t&size,&constchar*pszFile,&intnLine) &  { &  void*&pRet&=&malloc(size); &  Log(Type_Malloc,&pRet,&size,&nLine,&pszFile); &  returnpR &  } &  void*&callocb(size_t&num,&size_t&size,&constchar*pszFile,&intnLine) &  { &  void*&pRet&=&calloc(num,&size); &  Log(Type_Calloc,&pRet,&size,&nLine,&pszFile); &  returnpR &  } &  voidfreeb(void*memblock,&constchar*pszFile,&intnLine) &  { &  if(memblock) &  { &  Log(Type_Free,&memblock,&0,&0,&&NULL&); &  } &  free(memblock); &  } &  void*&reallocb(void*memblock,&size_t&size,&constchar*pszFile,&intnLine) &  { &  void*&pR &  pRet&=&realloc(memblock,&size); &  Log(Type_Free,&memblock,&size,&nLine,&pszFile); &  Log(Type_Realloc,&pRet,&size,&nLine,&pszFile); &  returnpR &  } &  void*&operatornew(size_t&size,&constchar*pszFile,&intnLine) &  { &  void*&pRet&=&malloc(size); &  Log(Type_New,&pRet,&size,&nLine,&pszFile); &  returnpR &  } &  void*&operatornew[](size_t&size,&constchar*pszFile,&intnLine) &  { &  void*&pRet&=&malloc(size); &  Log(Type_New_Array,&pRet,&size,&nLine,&pszFile); &  returnpR &  } &  //#include&&new&  voidoperatordelete(void*memblock)&throw() &  { &  if(memblock) &  { &  Log(Type_Delete,&memblock,&0,&0,&&NULL&); &  } &  free(memblock); &  } &  voidoperatordelete[](void*memblock)&throw() &  { &  if(memblock) &  { &  Log(Type_Delete_Array,&memblock,&0,&0,&&NULL&); &  } &  free(memblock); &  } &  voidpre_delete(constchar*pszFile,&intnLine) &  { &  }&
  注意:
  a. 输出的目录我是写死了,在D:MemLeak_Log
  b. 在被检查工程里面请增加/FC选项。Project-&Properties-&Configuration-&C/C++-&Advanced-&Use Full Path Yes(/FC)
  c. MemLeakLog.dll 拷贝到与被检查内存泄漏的进程所在的目录下面
  我附带上一个例子,大家一看就明白了。
  下载地址:
H3C认证Java认证Oracle认证
基础英语软考英语项目管理英语职场英语
.NETPowerBuilderWeb开发游戏开发Perl
二级模拟试题一级模拟试题一级考试经验四级考试资料
软件测试软件外包系统分析与建模敏捷开发
法律法规历年试题软考英语网络管理员系统架构设计师信息系统监理师
高级通信工程师考试大纲设备环境综合能力
路由技术网络存储无线网络网络设备
CPMP考试prince2认证项目范围管理项目配置管理项目管理案例项目经理项目干系人管理
职称考试题目
招生信息考研政治
网络安全安全设置工具使用手机安全
生物识别传感器物联网传输层物联网前沿技术物联网案例分析
Java核心技术J2ME教程
Linux系统管理Linux编程Linux安全AIX教程
Windows系统管理Windows教程Windows网络管理Windows故障
数据库开发Sybase数据库Informix数据库
&&&&&&&&&&&&&&&
希赛网 版权所有 & &&酷勤网 C 程序员的那点事!
当前位置: >
浏览次数:次
是检测App内存泄露的工具, 内存泄露是Android开发中常见的问题, 使用程序的稳定性下降.
注意: 集成到低版本应用, 会报出Bug.
Error:(2) Error retrieving parent for item: No resource found that matches the given name 'android:Theme.Material'.
修改编译版本:compileSdkVersion 21即可.
本文示例的Github.
生命周期较长的类使用Activity的Context, 导致Activity被引用, 无法被及时回收. 除了需要Activity页面支持的控件类, 如Dialog等, 其他全部使用应用的Context替换Activity的Context, 即Context.getApplicationContext(). 还有就是单例不要持有页面的控件, 单例持有控件, 控件附属页面, 最终页面得不到释放, 单例可以使用回调修改页面, 内部仅仅保留处理数据部分.
LeakCanary可以检查出页面的泄露问题, 并提供具体位置.
表明: LeakSingle的静态单例sInstance引用LeakSingle的mContext, 从而导致MainActivity的instance泄露.
内存泄露的原因已经理解了, 那么我来讲解一下如何检测应用吧.
1. 依赖和引入
build.gradle的依赖.
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1' // or 1.4-beta1
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' // or 1.4-beta1
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' // or 1.4-beta1
public class DemoApplication extends Application {
@Override public void onCreate() {
super.onCreate();
LeakCanary.install(this);
配置非常简单, 会增加一个附属应用, 去掉Application的引用, 就可以移除LeakCanary.
2. 泄露单例
泄露单例, 引入页面的TextView, 强制保留的父Activity, 会导致内存泄露, 可以在onDestroy时, 解引用避免.
* 泄露单例, 设计混乱, 单例只应该做事务性的工作, 页面操作应该使用回调.
* 本示例仅做为反面示例, 切勿学习.
* Created by wangchenlong on 16/1/25.
*/ public class LeakSingle {
private Context mC
private TextView mTextV
private static LeakSingle sI
private LeakSingle(Context context) {
mContext =
public static LeakSingle getInstance(Context context) {
if (sInstance == null) {
sInstance = new LeakSingle(context);
// 内存泄露
public void setRetainedTextView(TextView tv) {
mTextView =
mTextView.setText(mContext.getString(R.string.app_name));
// 删除引用, 防止泄露
public void removeRetainedTextView() {
mTextView =
单例只应该做事务性的工作, 页面操作应该使用回调, 不是引入控件. 本示例仅做为反面示例, 切勿学习.
3. 泄露内存
调用单例, 两种引用都会导致内存泄露, 第一种是Context引用泄露, 第二种是子控件引用泄露. 避免方式是在onDestroy中, 清除引用.
在单例中只执行事务性工作, 不执行具体页面操作, 可以使用接口回调, 异步处理.
public class MainActivity extends AppCompatActivity {
@Bind(R.id.main_tv_text) TextView mTvT
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
* me.chunyu.spike.wcl_leakcanary_demo.MainActivity has leaked:
* GC ROOT static me.chunyu.spike.wcl_leakcanary_demo.LeakSingle.sInstance
* references me.chunyu.spike.wcl_leakcanary_demo.LeakSingle.mContext
* leaks me.chunyu.spike.wcl_leakcanary_demo.MainActivity instance
LeakSingle.getInstance(this).setRetainedTextView(mTvText);
* me.chunyu.spike.wcl_leakcanary_demo.MainActivity has leaked:
* GC ROOT static me.chunyu.spike.wcl_leakcanary_demo.LeakSingle.sInstance
* references me.chunyu.spike.wcl_leakcanary_demo.LeakSingle.mTextView
* references android.support.v7.widget.AppCompatTextView.mContext
* leaks me.chunyu.spike.wcl_leakcanary_demo.MainActivity instance
LeakSingle.getInstance(this.getApplication()).setRetainedTextView(mTvText);
@Override protected void onDestroy() {
super.onDestroy();
// 防止内泄露
LeakSingle.getInstance(this.getApplication()).removeRetainedTextView();
根据LeakCanary中的检测结果, 修改内存泄露的地方, 就可以完美的解决问题.
4. 解析泄露位置
新手经常会问我, 如何理解LeakCanary的错误信息, 我来举个例子讲解一些.
LeakCanary的内存泄露提示一般会包含三个部分:
第一部分(LeakSingle类的sInstance变量)引用第二部分(LeakSingle类的mContext变量), 导致第三部分(MainActivity类的实例instance)泄露.
应用最常见的泄露位置就是Activity的实例, 要手动或使用shell命令, 启动所有的Activity. LeakCanary判断时机是Activity启动到结束, 检测这一过程是否会发生内存泄露.
启动Activity的shell命令是
adb shell am start
-n [包名]/[Activity名]
内存泄露的问题对于应用的用户体验至关重要, 感谢Square的产品, 让这件事变得如此简单.
OK, that's all! Enjoy it!
& 相关主题:

我要回帖

更多关于 ios检查内存泄露 的文章

 

随机推荐