cv mat 矩阵 最大可定义为多少 mat工具 分析内存泄漏漏

1161人阅读
一、MAT是什么?
& & & MAT(Memory Analyzer Tool),一个基于Eclipse的内存分析工具,是一个快速、功能丰富的JAVA heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗。使用内存分析工具从众多的对象中进行分析,快速的计算出在内存中对象的占用大小,看看是谁阻止了垃圾收集器的回收工作,并可以通过报表直观的查看到可能造成这种结果的对象。
二、如何使用
使用的方法将dump文件导入然后进行分析。
方法如下:
1、通过JMX的MBean生成当前的Heap信息,大小为一个3G(整个堆的大小)的hprof文件,如果没有启动JMX可以通过Java的JMAP命令来生成该文件。
2、 要考虑的是如何打开这个DUMP的堆信息文件,显然一般的Window系统没有这么大的内存,必须借助高配置的Linux。当然我们可以借助X-Window把Linux上的图形导入到Window。我们考虑用下面几种工具打开该文件:
Visual VM、IBM HeapAnalyzer、JDK 自带的Hprof工具。
& & & &使用这些工具时为了确保加载速度,建议设置最大内存为6G。使用后发现,这些工具都无法直观地观察到内存泄漏,Visual VM虽能观察到对象大小,但看不到调用堆栈;HeapAnalyzer虽然能看到调用堆栈,却无法正确打开一个3G的文件。可以使用MAT工具直接导入文件,生成图表信息和疑似有问题的JAVA类,如下图所示:
***************************原文结束,学习笔记开始***************************
之前的关于OOM异常的学习笔记提到过,对于出现内存泄露等疑难问题,需要保留日志,mat进行分析。
那就开搞吧,体会下过程。
1安装mat。
要是MyEclipse,网上有教程怎么安装,我本机安装的事Spring Tool Suite &Version: 3.5.0.RELEASE.
也可以从sts - &help&- & Install New Software。
https://www.eclipse.org/mat/downloads.php&
这是下载页面。里面有对应的地址。copy出来。
在workwith输入。选择第一个,然后一路安装见下图。
console安装完提示重启.
又发现一个问题。jdk我安装的事64位,但是sts是同事拷的32位的,所以下载的插件也是32位的,没法运行,悲剧了
没办法,直接下载MemoryAnalyzer-1.5.0.-win32.win32.x86_64.zip。解压缩后直接运行。
&****************安装完成,进入分析dump阶段******************
找个以前的dump看看。结果打开就报错。
!ENTRY org.eclipse.mat.ui 4 0
14:41:27.773
!MESSAGE Error opening heap dump '31615.dump'. Check the error log for further details.
org.eclipse.mat.SnapshotException: Error opening heap dump '31615.dump'. Check the error log for further details.
at org.eclipse.mat.parser.internal.SnapshotFactoryImpl.parse(SnapshotFactoryImpl.java:268)
at org.eclipse.mat.parser.internal.SnapshotFactoryImpl.openSnapshot(SnapshotFactoryImpl.java:126)
at org.eclipse.mat.snapshot.SnapshotFactory.openSnapshot(SnapshotFactory.java:145)
at org.eclipse.mat.ui.snapshot.ParseHeapDumpJob.run(ParseHeapDumpJob.java:83)
at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55)
Caused by: java.io.IOException: Invalid HPROF file header.
at org.eclipse.mat.hprof.AbstractParser.readVersion(AbstractParser.java:143)
at org.eclipse.mat.hprof.Pass1Parser.read(Pass1Parser.java:84)
at org.eclipse.mat.hprof.HprofIndexBuilder.fill(HprofIndexBuilder.java:80)
at org.eclipse.mat.parser.internal.SnapshotFactoryImpl.parse(SnapshotFactoryImpl.java:222)
... 4 more
& 上网搜了下,Google有类似报错,我理解mat只能打开二进制文件。
还没测试完呢不能放弃,自己去生成一个吧。jmap ,这次注意了加了format=b
&果然比较大,900多M。下载到本机。
-rw------- 1 cloud-user cloud-user 907M Mar &9 15:26 8423
打开刚才下载文件。“File-&Open
heap dump...”打开指定的dump文件后,将会生成Overview选项,
,当然对于我的烂电脑来说是个有点漫长的过程,内存快满了,期间很卡。如下图所示:
&在Overview选项中,以饼状图的形式列举出了程序内存消耗的一些基本信息,其中每一种不同颜色的饼块都代表了不同比例的内存消耗情况。
&&&& 1. Histogram可以列出内存中的对象,对象的个数以及大小。
&&&& 2. Dominator Tree可以列出那个线程,以及线程下面的那些对象占用的空间。
&&&& 3.Top consumers通过图形列出最大的object。
&&&& 4.Leak Suspects通过MA自动分析泄漏的原因。
最后,再来看看&Leak Suspects
System Overview
One instance of &java.util.concurrent.ArrayBlockingQueue& loaded by
&&system class loader&& occupies 43,215,064 (67.49%) bytes. The instance is referenced by
com.hshbic.uplus.collect.Thread.QueueObject @ 0x60c89c0d8&, loaded by
&sun.misc.Launcher$AppClassLoader @ 0x60c0fdfe0&. The memory is accumulated in one instance of
&java.lang.Object[]& loaded by &&system class loader&&.
java.util.concurrent.ArrayBlockingQueue
java.lang.Object[]
sun.misc.Launcher$AppClassLoader @ 0x60c0fdfe0
点击details,可以查看详情
以上只是熟悉下过程。记录下使用心得。
我想在实际应用中,也是一问题为导向,如果看到频繁触发GC,就是借助工具看看是那些类占用内存大小,影响回收。
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:111784次
积分:1765
积分:1765
排名:千里之外
原创:143篇
转载:18篇
(1)(18)(2)(3)(2)(1)(10)(17)(10)(97)
(window.slotbydup = window.slotbydup || []).push({
id: '4740887',
container: s,
size: '250,250',
display: 'inlay-fix'&nbsp&#8250&nbsp&nbsp&#8250&nbsp
Android 性能优化之使用MAT分析内存泄露问题
我们平常在开发Android应用程序的时候,稍有不慎就有可能产生OOM,虽然JAVA有垃圾回收机,但也不能杜绝内存泄露,内存溢出等问题,随
着科技的进步,移动设备的内存也越来越大了,但由于Android设备的参差不齐,可能运行在这台设备好好的,运行在那台设备就报OOM,这些适配问题也
是比较蛋疼的,比如我们平常运行着一个应用程序,运行的好好的,突然到某个Activity就给你爆出一个OOM的错误,你可能会以为是这个
Activity导致的内存泄露,你会想到也有可能是内存有泄露吗?内存泄露就像一个定时炸弹,随时都有可能使我们的应用程序崩溃掉,所以作为一名
Android开发人员,还是需要有分析内存泄露的能力,说道这里我们还是要说下什么是内存泄露,内存泄露是指有个引用指向一个不再被使用的对象,导致该
对象不会被垃圾回收器回收。因此,垃圾回收器是无法回收内存泄露的对象。本文就使用DDMS(Dalvik Debug Monitor
Server)和MAT(Memory Analyzer Tool)工具带大家来分析内存泄露问题。工具的准备DDMS是ADT自带的调试工具,有关DDMS的使用请参考,
而MAT的就需要我们自行安装Eclipse插件,安装方法我就不多说了,下面给出一个在线安装的地
址:http://download.eclipse.org/mat/1.3/update-site/,MAT可以检测到内存泄露,降低内存消耗,它
有着非常强大的解析堆内存空间dump能力。如何检测内存泄露1.使用DDMS检测内存泄露打开Devices视图,选择我们需要分析的应用程序进程,点击Updata Heap按钮然后在打开DDMS, 选择Heap标签,然后点击Cause GC按钮,点击Cause GC是手动触发JAVA垃圾回收器,如下图如
果我们要测试某个Activity是否发生内存泄露,我们可以反复进入和退出这个Activity, 再手动触发几次垃圾回收,观察上图中 data
object这一栏中的 Total
Size的大小是保持稳定还是有明显的变大趋势,如果有明显的变大趋势就说明这个Activity存在内存泄露的问题,我们就需要在具体分析。2.使用Logcat检测内存泄露当垃圾回收机在进行垃圾回收之后,会在Logcat中作相对于的输出,所以我们也可以通过这些信息来判断是否存在内存泄露问题一,上面消息的第一个部分产生GC的原因,一共有四种类型GC_CONCURRENT &&当你的堆内存快被用完的时候,就会触发这个GC回收GC_FOR_MALLOC &堆内存已经满了,同时又要试图分配新的内存,所以系统要回收内存GC_EXTERNAL_ALLOC &&在Android3.0 (Honeycomb)以前,释放通过外部内存(比如在2.3以前,产生的Bitmap对象存储在Native Memory中)时产生。Android3.0和更高版本中不再有这种类型的内存分配了。GC_EXPLICIT &调用System.gc时产生,上图中就是点击Cause GC按钮手动触发垃圾回收器产生的log信息二,freed 1413K表示GC释放了1434K的内存三,20% free K, 20%表示目前可分配内存占的比例,9349K表示当前活动对象所占内存,11644K表示Heap的大小四,paused 8ms + 3ms, total 71ms,则表示触发GC应用暂停的时间和GC总共消耗的时间有了这些log信息,我们就可以知道GC运行几次以后有没有成功释放出一些内存,如果分配出去的内存在持续增加,那么很明显存在内存泄露,如下存在内存泄露的Log信息很明显Heap中空闲内存占总Heap的比例在缩小,Heap中活动对象所占的内存在增加。内存泄露分析实战下面是一个存在内存泄露的例子代码,这也是比较常见的一种内存泄露的方式,就是在Activity中写一些内部类,并且这些内部类具有生命周期过长的现象package&com.example.
import&java.util.ArrayL
import&java.util.L
import&android.app.A
import&android.os.B
public&class&LeakActivity&extends&Activity&{
&&&&private&List&String&&list&=&new&ArrayList&String&();
&&&&@Override
&&&&protected&void&onCreate(Bundle&savedInstanceState)&{
&&&&&&&&super.onCreate(savedInstanceState);
&&&&&&&&//模拟Activity一些其他的对象
&&&&&&&&for(int&i=0;&i&10000;i++){
&&&&&&&&&&&&list.add(&Memory&Leak!&);
&&&&&&&&//开启线程
&&&&&&&&new&MyThread().start();
&&&&public&class&MyThread&extends&Thread{
&&&&&&&&@Override
&&&&&&&&public&void&run()&{
&&&&&&&&&&&&super.run();
&&&&&&&&&&&&
&&&&&&&&&&&&//模拟耗时操作
&&&&&&&&&&&&try&{
&&&&&&&&&&&&&&&&Thread.sleep(10&*&60&*&1000);
&&&&&&&&&&&&}&catch&(InterruptedException&e)&{
&&&&&&&&&&&&&&&&e.printStackTrace();
&&&&&&&&&&&&}
&&&&&&&&&&&&
}运行例子代码,选择Devices视图,点击上面Updata Heap标签,然后再旋转屏幕,多重复几次,然后点击Dump HPROF file, 之后Eclipse的MAT插件会自动帮我们打开,如下图我们看到下面有Histogram(直方图)他列举了每个对象的统计,Dominator Tree(支配树)提供了程序中最占内存的对象的排列,这两个是我在排查内存泄露的时候用的最多的Histogram(直方图)我
们先来看Histogram,&MAT最有用的工具之一,它可以列出任意一个类的实例数。它支持使用正则表达式来查找某个特定的类,还可以计算出该类所有
对象的保留堆最小值或者精确值, 我们可以通过正则表达式输入LeakActivity,
看到Histogram列出了与LeakActivity相关的类我
们可以看到LeakActivity,和MyThread内部类都存在16个对象,虽然LeakActivity和MyThread存在那么多对象,但是
到这里并不能让我们准确的判断这两个对象是否存在内存泄露问题,
选中com.example.memoryleak.LeakActivity,点击右键,如下图Merge
Shortest Paths to GC Roots 可以查看一个对象到RC &Roots是否存在引用链相连接,
在JAVA中是通过可达性(Reachability Analysis)来判断对象是否存活,这个算法的基本思想是通过一系列的称谓&GC
Roots&的对象作为起始点,从这些节点开始向下搜索,搜索所走得路径称为引用链,当一个对象到GC
Roots没有任何引用链相连则该对象被判定为可以被回收的对象,反之不能被回收,我们可以选择 exclude all
phantom/weak/soft
etc.references(排查虚引用/弱引用/软引用等)因为被虚引用/弱引用/软引用的对象可以直接被GC给回收.可以看到LeakActivity存在GC Roots链,即存在内存泄露问题,可以看到LeakActivity被MyThread的this$0持有。除了使用Merge Shortest Paths to GC Roots 我们还可以使用List object - With outgoing References & 显示选中对象持有那些对象List object -&With incoming References &显示选中对象被那些外部对象所持有Show object by class - With outgoing References &显示选中对象持有哪些对象, 这些对象按类合并在一起排序Show object by class - With incoming References &显示选中对象被哪些外部对象持有, 这些对象按类合并在一起排序Dominator Tree(支配树)它可以将所有对象按照Heap大小排序显示, 使用方法跟Histogram(直方图)差不多,在这里我就不做过多的介绍了我
们知道上面的例子代码中我们知道内部类会持有外部类的引用,如果内部类的生命周期过长,会导致外部类内存泄露,那么你会问,我们应该怎么写那不会出现内存
泄露的问题呢?既然内部类不行,我们就外部类或者static的内部类,如果我们需要用到外部类里面的一些东西,我们可以将外部类Weak
Reference传递进去package&com.example.
import&java.lang.ref.WeakR
import&java.util.ArrayL
import&java.util.L
import&android.app.A
import&android.os.B
public&class&LeakActivity&extends&Activity&{
&&&&private&List&String&&list&=&new&ArrayList&String&();
&&&&@Override
&&&&protected&void&onCreate(Bundle&savedInstanceState)&{
&&&&&&&&super.onCreate(savedInstanceState);
&&&&&&&&//模拟Activity一些其他的对象
&&&&&&&&for(int&i=0;&i&10000;i++){
&&&&&&&&&&&&list.add(&Memory&Leak!&);
&&&&&&&&//开启线程
&&&&&&&&new&MyThread(this).start();
&&&&public&static&class&MyThread&extends&Thread{
&&&&&&&&private&WeakReference&LeakActivity&&mLeakActivityR
&&&&&&&&public&MyThread(LeakActivity&activity){
&&&&&&&&&&&&mLeakActivityRef&=&new&WeakReference&LeakActivity&(activity);
&&&&&&&&@Override
&&&&&&&&public&void&run()&{
&&&&&&&&&&&&super.run();
&&&&&&&&&&&&
&&&&&&&&&&&&//模拟耗时操作
&&&&&&&&&&&&try&{
&&&&&&&&&&&&&&&&Thread.sleep(10&*&60&*&1000);
&&&&&&&&&&&&}&catch&(InterruptedException&e)&{
&&&&&&&&&&&&&&&&e.printStackTrace();
&&&&&&&&&&&&}
&&&&&&&&&&&&
&&&&&&&&&&&&//如果需要使用LeakActivity,我们需要添加一个判断
&&&&&&&&&&&&LeakActivity&activity&=&mLeakActivityRef.get();
&&&&&&&&&&&&if(activity&!=&null){
&&&&&&&&&&&&&&&&//do&something
&&&&&&&&&&&&}
&&&&&&&&&&&&
}同理,Handler也存在同样的问题,比如下面的代码package&com.example.
import&android.app.A
import&android.os.B
import&android.os.H
import&android.os.M
public&class&LeakActivity&extends&Activity&{
&&&&@Override
&&&&protected&void&onCreate(Bundle&savedInstanceState)&{
&&&&&&&&super.onCreate(savedInstanceState);
&&&&&&&&MyHandler&handler&=&new&MyHandler();
&&&&&&&&handler.sendMessageDelayed(Message.obtain(),&10&*&60&*&1000);
&&&&public&class&MyHandler&extends&Handler{
&&&&&&&&@Override
&&&&&&&&public&void&handleMessage(Message&msg)&{
&&&&&&&&&&&&super.handleMessage(msg);
}我们知道使用MyHandler发送消息的时候,Message会被加入到主线程的MessageQueue里面,而每条Message的target会
持有MyHandler对象,而MyHandler的this$0又会持有LeakActivity对象,
所以我们在旋转屏幕的时候,由于每条Message被延迟了 10分钟,所以必然会导致LeakActivity泄露,所以我们需要将代码进行修改下package&com.example.
import&java.lang.ref.WeakR
import&android.app.A
import&android.os.B
import&android.os.H
import&android.os.M
public&class&LeakActivity&extends&Activity&{
&&&&MyHandler&
&&&&@Override
&&&&protected&void&onCreate(Bundle&savedInstanceState)&{
&&&&&&&&super.onCreate(savedInstanceState);
&&&&&&&&handler&=&new&MyHandler(this);
&&&&&&&&handler.sendMessageDelayed(Message.obtain(),&10&*&60&*&1000);
&&&&public&static&class&MyHandler&extends&Handler{
&&&&&&&&public&WeakReference&LeakActivity&&mLeakActivityR
&&&&&&&&public&MyHandler(LeakActivity&leakActivity)&{
&&&&&&&&&&&&mLeakActivityRef&=&new&WeakReference&LeakActivity&(leakActivity);
&&&&&&&&@Override
&&&&&&&&public&void&handleMessage(Message&msg)&{
&&&&&&&&&&&&super.handleMessage(msg);
&&&&&&&&&&&&
&&&&&&&&&&&&if(mLeakActivityRef.get()&!=&null){
&&&&&&&&&&&&&&&&//do&something
&&&&&&&&&&&&}
&&&&@Override
&&&&protected&void&onDestroy()&{
&&&&&&&&handler.removeCallbacksAndMessages(null);
&&&&&&&&super.onDestroy();
}上面的代码就能保证LeakActivity不会被泄露,注意我们在Activity的onDestory方法中使用了handler.removeCallbacksAndMessages(null),这样子能保证LeakActivity退出的时候,每条Message的target &MyHandler也会被释放, 所以我们在使用非static的内部类的时候,要注意该内部类的生命周期是否比外部类要长,如果是的话我们可以使用上面的解决方法。常见的内存泄露问题1.上面两种情形2.资源对象没有关闭,比如数据库操作中得Cursor,IO操作的对象3.调用了registerReceiver注册广播后未调用unregisterReceiver()来取消4.调用了View.getViewTreeObserver().addOnXXXListener ,而没有调用View.getViewTreeObserver().removeXXXListener5.Android 3.0以前,没有对不在使用的Bitmap调用recycle(),当然在Android 3.0以后就不需要了,更详细的请查看6.Context的泄露,比如我们在单例类中使用Context对象,如下import&android.content.C
public&class&Singleton&{
&&&&private&Context&
&&&&private&static&Singleton&mS
&&&&private&Singleton(Context&context){
&&&&&&&&this.context&=&
&&&&public&static&Singleton&getInstance(Context&context){
&&&&&&&&if(mSingleton&==&null){
&&&&&&&&&&&&synchronized&(Singleton.class)&{
&&&&&&&&&&&&&&&&if(mSingleton&==&null){
&&&&&&&&&&&&&&&&&&&&mSingleton&=&new&Singleton(context);
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&}
&&&&&&&&return&mS
}假如我们在某个Activity中使用Singleton.getInstance(this)或者该实例,那么会造成该Activity一直被Singleton对象引用着,所以这时候我们应该使用getApplicationContext()来代替Activity的Context,getApplicationContext()获取的Context是一个全局的对象,所以这样就避免了内存泄露。相同的还有将Context成员设置为static也会导致内存泄露问题。7.
不要重写finalize()方法,我们有时候可能会在某个对象被回收前去释放一些资源,可能会在finalize()方法中去做,但是实现了
finalize的对象,创建和回收的过程都更耗时。创建时,会新建一个额外的Finalizer 对象指向新创建的对象。
而回收时,至少需要经过两次GC,第一次GC检测到对象只有被Finalizer引用,将这个对象放入
Finalizer.ReferenceQueue 此时,因为Finalizer的引用,对象还无法被GC,Finalizer$FinalizerThread 会不停的清理Queue的对象,remove掉当前元素,并执行对象的finalize方法,清理后对象没有任何引用,在下一次GC被回收,所以说该对象存活时间更久,导致内存泄露。原文 &
上一篇: 前几篇文章中,我们知道了如何创建 VectorDrawable 以及制作path group的动画,也知道了如何渲染出路径的绘制效果。但是还有更多的动画方式,那就是修改SVG的path数据本身。这篇文章将讲解是如何做到的。 为了修改SVG的path数据,我们需要先了解一点SVG path
下一篇: 安卓开发中,在写布局代码的时候,ide可以看到布局的预览效果。 但是有些效果则必须在运行之后才能看见,比如这种情况:TextView在xml中没有设置任何字符,而是在activity中设置了text。因此为了在ide中预览效果,你必须在xml中为TextView控件设置android:te{"debug":false,"apiRoot":"","paySDK":"/api/js","wechatConfigAPI":"/api/wechat/jssdkconfig","name":"production","instance":"column","tokens":{"X-XSRF-TOKEN":null,"X-UDID":null,"Authorization":"oauth c3cef7c66aa9e6a1e3160e20"}}
{"database":{"Post":{"":{"contributes":[{"sourceColumn":{"lastUpdated":,"description":"贝聊移动开发部自留地,种iOS,Android,前端,测试,育儿以及其他相关庄稼~","permission":"COLUMN_PUBLIC","memberId":163888,"contributePermission":"COLUMN_PUBLIC","translatedCommentPermission":"all","canManage":true,"intro":"贝聊移动开发团队","urlToken":"ibeiliao","id":40710,"imagePath":"v2-23ccda88bd26b.jpg","slug":"ibeiliao","applyReason":"0","name":"贝聊移动开发","title":"贝聊移动开发","url":"/ibeiliao","commentPermission":"COLUMN_ALL_CAN_COMMENT","canPost":true,"created":,"state":"COLUMN_NORMAL","followers":184,"avatar":{"id":"v2-23ccda88bd26b","template":"/{id}_{size}.jpg"},"activateAuthorRequested":false,"following":false,"imageUrl":"/v2-23ccda88bd26b_l.jpg","articlesCount":16},"state":"accepted","targetPost":{"titleImage":"/v2-ecc5ecb0469_r.jpg","lastUpdated":,"imagePath":"v2-ecc5ecb0469.jpg","permission":"ARTICLE_PUBLIC","topics":[],"summary":"1.Java内存分配策略 Java 程序运行时的内存分配策略有三种:静态分配、栈式分配和堆式分配。对应的存储区域如下: 静态存储区(方法区):主要存放静态数据、全局 static 数据和常量。这块内存在程序编译时就已经分配好,并且在程序整个运行期间都存在。 栈…","copyPermission":"ARTICLE_COPYABLE","translatedCommentPermission":"all","likes":0,"origAuthorId":0,"publishedTime":"T10:55:30+08:00","sourceUrl":"","urlToken":,"id":3224799,"withContent":false,"slug":,"bigTitleImage":false,"title":"使用Android Studio和MAT进行内存泄漏分析","url":"/p/","commentPermission":"ARTICLE_ALL_CAN_COMMENT","snapshotUrl":"","created":,"comments":0,"columnId":0,"content":"","parentId":0,"state":"ARTICLE_PUBLISHED","imageUrl":"/v2-ecc5ecb0469_r.jpg","author":{"bio":null,"isFollowing":false,"hash":"4d57b5fffa7f97d1a551","uid":72,"isOrg":false,"slug":"xun-chang-xiang-mo-77","isFollowed":false,"description":"","name":"寻常巷陌","profileUrl":"/people/xun-chang-xiang-mo-77","avatar":{"id":"c70f40ac71f1","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},"memberId":482618,"excerptTitle":"","voteType":"ARTICLE_VOTE_CLEAR"},"id":698749}],"title":"使用Android Studio和MAT进行内存泄漏分析","author":"xun-chang-xiang-mo-77","content":"1.Java内存分配策略Java 程序运行时的内存分配策略有三种:静态分配、栈式分配和堆式分配。对应的存储区域如下:静态存储区(方法区):主要存放静态数据、全局 static 数据和常量。这块内存在程序编译时就已经分配好,并且在程序整个运行期间都存在。栈区 :方法体内的局部变量都在栈上创建,并在方法执行结束时这些局部变量所持有的内存将会自动被释放。堆区 : 又称动态内存分配,通常就是指在程序运行时直接 new 出来的内存。这部分内存在不使用时将会由 Java 垃圾回收器来负责回收。2.堆与栈的区别栈内存:在方法体内定义的局部变量(一些基本类型的变量和对象的引用变量)都是在方法的栈内存中分配的。当在一段方法块中定义一个变量时,Java 就会在栈中为该变量分配内存空间,当超过该变量的作用域后,分配给它的内存空间也将被释放掉,该内存空间可以被重新使用。堆内存:用来存放所有由 new 创建的对象(包括该对象其中的所有成员变量)和数组。在堆中分配的内存,将由 Java 垃圾回收器来自动管理。在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中的首地址,这个特殊的变量就是我们上面说的引用变量。我们可以通过这个引用变量来访问堆中的对象或者数组。例子:public class A {\n\n
int a = 0;\n\n
B b = new B();\n\n
public void test(){\n
int a1 = 1;\n
B b1 = new B();\n
}\n\n}\n\nA object = new A();\nA类内的局部变量都存在于栈中,包括基本数据类型a1和引用变量b1,b1指向的B对象实体存在于堆中引用变量object存在于栈中,而object指向的对象实体存在于堆中,包括这个对象的所有成员变量a和b,而引用变量b指向的B类对象实体存在于堆中3.Java管理内存的机制Java的内存管理就是对象的分配和释放问题。内存的分配是由程序员来完成,内存的释放由GC(垃圾回收机制)完成。GC 为了能够正确释放对象,必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等。这是Java程序运行较慢的原因之一。释放对象的原则:该对象不再被引用。GC的工作原理:将对象考虑为有向图的顶点,将引用关系考虑为有向图的有向边,有向边从引用者指向被引对象。另外,每个线程对象可以作为一个图的起始顶点,例如大多程序从 main 进程开始执行,那么该图就是以 main 进程为顶点开始的一棵根树。在有向图中,根顶点可达的对象都是有效对象,GC将不回收这些对象。如果某个对象与这个根顶点不可达,那么我们认为这个对象不再被引用,可以被 GC 回收。下面举一个例子说明如何用有向图表示内存管理。对于程序的每一个时刻,我们都有一个有向图表示JVM的内存分配情况。以下右图,就是左边程序运行到第6行的示意图。另外,Java使用有向图的方式进行内存管理,可以消除引用循环的问题,例如有三个对象相互引用,但只要它们和根进程不可达,那么GC也是可以回收它们的。当然,除了有向图的方式,还有一些别的内存管理技术,不同的内存管理技术各有优缺点,在这里就不详细展开了。4.Java中的内存泄漏如果一个对象满足以下两个条件:(1)这些对象是可达的,即在有向图中,存在通路可以与其相连(2)这些对象是无用的,即程序以后不会再使用这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,继续占用着内存。在C++中,内存泄漏的范围更大一些。有些对象被分配了内存空间,然后却不可达,由于C++中没有GC,这些内存将永远收不回来。在Java中,这些不可达的对象都由GC负责回收,因此程序员不需要考虑这部分的内存泄漏。5.Android中常见的内存泄漏(1)单例造成的内存泄漏这是一个普通的单例模式,当创建这个单例的时候,由于需要传入一个Context,所以这个Context的生命周期的长短至关重要:这是一个普通的单例模式,当创建这个单例的时候,由于需要传入一个Context,所以这个Context的生命周期的长短至关重要:1.如果此时传入的是 Application 的 Context,因为 Application 的生命周期就是整个应用的生命周期,所以没有任何问题。2.如果此时传入的是 Activity 的 Context,当这个 Context 所对应的 Activity 退出时,由于该 Context 的引用被单例对象所持有,其生命周期等于整个应用程序的生命周期,所以当前 Activity 退出时它的内存并不会被回收,这就造成泄漏了。当然,Application 的 context 不是万能的,所以也不能随便乱用,例如Dialog必须使用 Activity 的 Context。对于这部分有兴趣的读者可以自行搜索相关资料。(2)非静态内部类创建静态实例造成的内存泄漏public class MainActivity extends AppCompatActivity {\n\n
private static TestResource mResource = null;\n
@Override\n
protected void onCreate(Bundle savedInstanceState) {\n
super.onCreate(savedInstanceState);\n
setContentView(R.layout.activity_main);\n
if(mManager == null){\n
mManager = new TestResource();\n
class TestResource {//...\n
}\n}\n非静态内部类默认会持有外部类的引用,而该非静态内部类又创建了一个静态的实例,该实例的生命周期和应用的一样长,这就导致了该静态实例一直会持有该Activity的引用,导致Activity的内存资源不能正常回收。(3)匿名内部类造成的内存泄漏匿名内部类默认也会持有外部类的引用。如果在Activity/Fragment中使用了匿名类,并被异步线程持有,如果没有任何措施这样一定会导致泄漏。ref1和ref2的区别是,ref2使用了匿名内部类。我们来看看运行时这两个引用的内存:ref1和ref2的区别是,ref2使用了匿名内部类。我们来看看运行时这两个引用的内存:可以看到,ref1没什么特别的。但ref2这个匿名类的实现对象里面多了一个引用:this$0这个引用指向MainActivity.this,也就是说当前的MainActivity实例会被ref2持有,如果将这个引用再传入一个异步线程,此线程和此Acitivity生命周期不一致的时候,就会造成Activity的泄漏。例子:Handler造成的内存泄漏在该 MainActivity 中声明了一个延迟10分钟执行的消息 Message,mHandler 将其 push 进了消息队列 MessageQueue 里。当该 Activity 被 finish() 掉时,延迟执行任务的 Message 还会继续存在于主线程中,它持有该 Activity 的 Handler 引用,然后又因 为 Handler 为匿名内部类,它会持有外部类的引用(在这里就是指 MainActivity),所以此时 finish() 掉的 Activity 就不会被回收了,从而造成内存泄漏。在该 MainActivity 中声明了一个延迟10分钟执行的消息 Message,mHandler 将其 push 进了消息队列 MessageQueue 里。当该 Activity 被 finish() 掉时,延迟执行任务的 Message 还会继续存在于主线程中,它持有该 Activity 的 Handler 引用,然后又因 为 Handler 为匿名内部类,它会持有外部类的引用(在这里就是指 MainActivity),所以此时 finish() 掉的 Activity 就不会被回收了,从而造成内存泄漏。修复方法:在 Activity 中避免使用非静态内部类或匿名内部类,比如将 Handler 声明为静态的,则其存活期跟 Activity 的生命周期就无关了。如果需要用到Activity,就通过弱引用的方式引入 Activity,避免直接将 Activity 作为 context 传进去。另外, Looper 线程的消息队列中还是可能会有待处理的消息,所以我们在 Activity 的 Destroy 时或者 Stop 时应该移除消息队列 MessageQueue 中的消息。见下面代码:(4)资源未关闭造成的内存泄漏对于使用了BraodcastReceiver,ContentObserver,File, Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。(5)一些不良代码造成的内存压力有些代码并不造成内存泄漏,但是它们,或是对没使用的内存没进行有效及时的释放,或是没有有效的利用已有的对象而是频繁的申请新内存。比如,Adapter里没有复用convertView等。6.Android中内存泄漏的排查与分析(1)利用Android Studio的Memory Monitor来检测内存情况先来看一下Android Studio 的 Memory Monitor界面:最原始的内存泄漏排查方式如下:最原始的内存泄漏排查方式如下:重复多次操作关键的可疑的路径,从内存监控工具中观察内存曲线,看是否存在不断上升的趋势,且退出一个界面后,程序内存迟迟不降低的话,可能就发生了严重的内存泄漏。这种方式可以发现最基本,也是最明显的内存泄漏问题,对用户价值最大,操作难度小,性价比极高。下面就开始用一个简单的例子来说明一下如何排查内存泄漏。首先,创建了一个TestActivity类,里面的测试代码如下:@Override\n
protected void processBiz() {\n
mHandler = new Handler();\n
mHandler.postDelayed(new Runnable() {\n
@Override\n
public void run() {\n
MLog.d(\"------postDelayed------\");\n
}, 800000L);\n
}\n运行项目,并执行以下操作:进入TestActivity,然后退出,再重新进入,如此操作几次后,最后最终退出TestActivity。这时发现,内存持续增高,如图所示:好了,这时我们可以假设,这里可能出现了内存泄漏的情况。那么,如何继续定位到内存泄漏的地址呢?这时候就得点击“Dump java heap”按钮来收集具体的信息了。(2)使用Android Studio生成Java Heap文件来分析内存情况注意,在点击 Dump java heap 按钮之前,一定要先点击Initate GC按钮强制GC,建议点击后等待几秒后再次点击,尝试多次,让GC更加充分。然后再点击Dump Java Heap按钮。这时候会生成一个Java heap文件并在新的窗口打开:这时候,点击右上角的“Analyzer Task”,再点击出现的绿色按钮,让Android studio帮我们自动分析出有可能潜在的内存泄漏的地方:这时候,点击右上角的“Analyzer Task”,再点击出现的绿色按钮,让Android studio帮我们自动分析出有可能潜在的内存泄漏的地方:如上图所示,Android studio提示有3个TestActivity对象可能出现了内存泄漏。而且左边的Reference Tree(引用树),也大概列出了该实体类被引用的路径。如果是一些比较简单的内存泄漏情况,仅仅看这里就大概能猜到是哪里导致了内存泄漏。但如果是比较复杂的情况,还是推荐使用MAT工具(Memory Analyzer)来继续分析比较好。(3)使用Memory Analyzer(MAT)来分析内存泄漏MAT是Eclipse出品的一个插件,当然也有独立的版本。下载链接:在这里先提醒一下:MAT并不会准确地告诉我们哪里发生了内存泄漏,而是会提供一大堆的数据和线索,我们需要根据自己的实际代码和业务逻辑去分析这些数据,判断到底是不是真的发生了内存泄漏。MAT支持对标准格式的hprof文件进行内存分析,所以,我们要先在Android Studio里先把Java heap文件转成标准格式的hprof文件,具体步骤如下:点击左侧的capture,选择对应的文件,并右键选择“Export to standard .hprof”导出标准的hprof文件:导出标准的hprof文件后,在MAT工具里导入,则看到以下界面:MAT中提供了非常多的功能,这里我们只要学习几个最常用的就可以了。上图那个饼状图展示了最大的几个对象所占内存的比例,这张图中提供的内容并不多,我们可以忽略它。在这个饼状图的下方就有几个非常有用的工具了。Histogram:直方图,可以列出内存中每个对象的名字、数量以及大小。Dominator Tree:会将所有内存中的对象按大小进行排序,并且我们可以分析对象之间的引用结构。1)Dominator Tree从上图可以看到右边存在着3个参数。Retained Heap表示这个对象以及它所持有的其它引用(包括直接和间接)所占的总内存,因此从上图中看,前两行的Retained Heap是最大的,分析内存泄漏时,内存最大的对象也是最应该去怀疑的。另外大家应该可以注意到,在每一行的最左边都有一个文件型的图标,这些图标有的左下角带有一个红色的点,有的则没有。带有红点的对象就表示是可以被GC Roots访问到的,可以被GC Root访问到的对象都是无法被回收的。那么这就可以说明所有带红色的对象都是泄漏的对象吗?当然不是,因为有些对象系统需要一直使用,本来就不应该被回收。如果发现有的对象右边有写着System Class,那么说明这是一个由系统管理的对象,并不是由我们自己创建并导致内存泄漏的对象。根据我们在Android studio的Java heap文件的提示,TestActivity对象有可能发生了内存泄漏,于是我们直接在上面搜TestActivity(这个搜索功能也是很强大的):左边的inspector可以查看对象内部的各种信息:当然,如果你觉得按照默认的排序方式来查看不方便,你可以自行设置排序的方式:Group by classGroup by class loaderGroup by package从上图可以看出,我们搜出了3个TestActivity的对象,一般在退出某个activity后,就结束了一个activity的生命周期,应该会被GC正常回收才对的。通常情况下,一个activity应该只有1个实例对象,但是现在居然有3个TestActivity对象存在,说明之前的操作,产生了3个TestActivity对象,并且无法被系统回收掉。从上图可以看出,我们搜出了3个TestActivity的对象,一般在退出某个activity后,就结束了一个activity的生命周期,应该会被GC正常回收才对的。通常情况下,一个activity应该只有1个实例对象,但是现在居然有3个TestActivity对象存在,说明之前的操作,产生了3个TestActivity对象,并且无法被系统回收掉。接下来继续查看引用路径。对着TestActivity对象点击右键 -& Merge Shortest Paths to GC Roots(当然,这里也可以选择Path To GC Roots) -& exclude all phantom/weak/soft etc. references为什么选择exclude all phantom/weak/soft etc. references呢?因为弱引用等是不会阻止对象被垃圾回收器回收的,所以我们这里直接把它排除掉接下来就能看到引用路径关系图了:从上图可以看出,TestActivity是被this$0所引用的,它实际上是匿名类对当前类的引用。this$0又被callback所引用,接着它又被Message中一串的next所引用...到这里,我们就已经分析出内存泄漏的原因了,接下来就是去改善存在问题的代码了。从上图可以看出,TestActivity是被this$0所引用的,它实际上是匿名类对当前类的引用。this$0又被callback所引用,接着它又被Message中一串的next所引用...到这里,我们就已经分析出内存泄漏的原因了,接下来就是去改善存在问题的代码了。2)Histogram这里是把当前应用程序中所有的对象的名字、数量和大小全部都列出来了,那么Shallow Heap又是什么意思呢?就是当前对象自己所占内存的大小,不包含引用关系的。这里是把当前应用程序中所有的对象的名字、数量和大小全部都列出来了,那么Shallow Heap又是什么意思呢?就是当前对象自己所占内存的大小,不包含引用关系的。上图当中,byte[]对象的Shallow Heap最高,说明我们应用程序中用了很多byte[]类型的数据,比如说图片。可以通过右键 -& List objects -& with incoming references来查看具体是谁在使用这些byte[]。当然,除了一般的对象,我们还可以专门查看线程对象的信息:Histogram中是可以显示对象的数量的,比如说我们现在怀疑TestActivity中有可能存在内存泄漏,就可以在第一行的正则表达式框中搜索“TestActivity”,如下所示:Histogram中是可以显示对象的数量的,比如说我们现在怀疑TestActivity中有可能存在内存泄漏,就可以在第一行的正则表达式框中搜索“TestActivity”,如下所示:接下来对着TestActivity右键 -& List objects -& with outgoing references查看具体TestActivity实例注:List objects -& with outgoing references :表示该对象的出节点(被该对象引用的对象)List objects -& with incoming references:表示该对象的入节点(引用到该对象的对象)如果想要查看内存泄漏的具体原因,可以对着任意一个TestActivity的实例右键 -& Merge Shortest Paths to GC Roots(当然,这里也可以选择Path To GC Roots) -& exclude all phantom/weak/soft etc. references,如下图所示:从这里可以看出,Histogram和Dominator Tree两种方式下操作都是差不多的,只是两种统计图展示的侧重点不太一样,实际操作中,根据需求选择不同的方式即可。3)两个hprof文件的对比为了排查内存泄漏,经常会需要做一些前后的对比。下面简单说一下两种对比方式:1.直接对比工具栏最右边有个“Compare to another heap dump”的按钮,只要点击,就可以生成对比后的结果。(注意,要先在MAT中打开要对比的hprof文件,才能选择对比的文件):2.添加到campare basket里对比在window菜单下面选择compare basket:在文件的Histogram view模式下,在navigation history下选择add to compare basket:然后就可以通过 Compare Tables 来进行对比了:7.总结最后,还是要再次提醒一下,工具是死的,人是活的,MAT也没有办法保证一定可以将内存泄漏的原因找出来,还是需要我们对程序的代码有足够多的了解,知道有哪些对象是存活的,以及它们存活的原因,然后再结合MAT给出的数据来进行具体的分析,这样才有可能把一些隐藏得很深的问题原因给找出来。参考资料:(1)(2) (3) (4)(5)","updated":"T02:55:30.000Z","canComment":false,"commentPermission":"anyone","commentCount":1,"collapsedCount":0,"likeCount":28,"state":"published","isLiked":false,"slug":"","lastestTipjarors":[],"isTitleImageFullScreen":false,"rating":"none","titleImage":"/v2-ecc5ecb0469_r.jpg","links":{"comments":"/api/posts//comments"},"reviewers":[],"topics":[{"url":"/topic/","id":"","name":"Android 开发"},{"url":"/topic/","id":"","name":"Android"}],"adminClosedComment":false,"titleImageSize":{"width":679,"height":275},"href":"/api/posts/","excerptTitle":"","tipjarState":"activated","tipjarTagLine":"真诚赞赏,手留余香","sourceUrl":"","pageCommentsCount":1,"tipjarorCount":0,"annotationAction":[],"hasPublishingDraft":false,"snapshotUrl":"","publishedTime":"T10:55:30+08:00","url":"/p/","lastestLikers":[{"bio":"互联网","isFollowing":false,"hash":"6ae717ff590c7e6c77f6","uid":453000,"isOrg":false,"slug":"bu-shu-yu-wo-de-ming-yun","isFollowed":false,"description":"","name":"不属于我的命运","profileUrl":"/people/bu-shu-yu-wo-de-ming-yun","avatar":{"id":"da8e974dc","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},{"bio":null,"isFollowing":false,"hash":"8afa7dd2f9cfc212addd28","uid":12,"isOrg":false,"slug":"souts-jjw","isFollowed":false,"description":"","name":"wxyz","profileUrl":"/people/souts-jjw","avatar":{"id":"da8e974dc","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},{"bio":"仙人抚我顶,结发受长生。","isFollowing":false,"hash":"b9a18e19b51855e3cadb5a3755188ffd","uid":80,"isOrg":false,"slug":"edisonq","isFollowed":false,"description":"科技成就生活之美!","name":"Edison Q","profileUrl":"/people/edisonq","avatar":{"id":"8c15ad607","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},{"bio":null,"isFollowing":false,"hash":"a6ab20ecfc85011a40bef2","uid":223400,"isOrg":false,"slug":"lynn-tan-90","isFollowed":false,"description":"","name":"三年以来","profileUrl":"/people/lynn-tan-90","avatar":{"id":"v2-ca21adec87a40fa734aac","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},{"bio":"不想再做一个没有时间码代码的码农。","isFollowing":false,"hash":"05da518e563977eba201c17dbe10ab95","uid":68,"isOrg":false,"slug":"huang-san-jun","isFollowed":false,"description":"","name":"君莫笑","profileUrl":"/people/huang-san-jun","avatar":{"id":"0f8639334","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false}],"summary":"1.Java内存分配策略 Java 程序运行时的内存分配策略有三种:静态分配、栈式分配和堆式分配。对应的存储区域如下: 静态存储区(方法区):主要存放静态数据、全局 static 数据和常量。这块内存在程序编译时就已经分配好,并且在程序整个运行期间都存在。 栈…","reviewingCommentsCount":0,"meta":{"previous":null,"next":null},"annotationDetail":null,"commentsCount":1,"likesCount":28,"FULLINFO":true}},"User":{"xun-chang-xiang-mo-77":{"isFollowed":false,"name":"寻常巷陌","headline":"","avatarUrl":"/c70f40ac71f1_s.jpg","isFollowing":false,"type":"people","slug":"xun-chang-xiang-mo-77","bio":null,"hash":"4d57b5fffa7f97d1a551","uid":72,"isOrg":false,"description":"","profileUrl":"/people/xun-chang-xiang-mo-77","avatar":{"id":"c70f40ac71f1","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false,"badge":{"identity":null,"bestAnswerer":null}}},"Comment":{},"favlists":{}},"me":{},"global":{},"columns":{"next":{}},"columnPosts":{},"columnSettings":{"colomnAuthor":[],"uploadAvatarDetails":"","contributeRequests":[],"contributeRequestsTotalCount":0,"inviteAuthor":""},"postComments":{},"postReviewComments":{"comments":[],"newComments":[],"hasMore":true},"favlistsByUser":{},"favlistRelations":{},"promotions":{},"switches":{"couldAddVideo":false},"draft":{"titleImage":"","titleImageSize":{},"isTitleImageFullScreen":false,"canTitleImageFullScreen":false,"title":"","titleImageUploading":false,"error":"","content":"","draftLoading":false,"globalLoading":false,"pendingVideo":{"resource":null,"error":null}},"drafts":{"draftsList":[],"next":{}},"config":{"userNotBindPhoneTipString":{}},"recommendPosts":{"articleRecommendations":[],"columnRecommendations":[]},"env":{"isAppView":false,"appViewConfig":{"content_padding_top":128,"content_padding_bottom":56,"content_padding_left":16,"content_padding_right":16,"title_font_size":22,"body_font_size":16,"is_dark_theme":false,"can_auto_load_image":true,"app_info":"OS=iOS"},"isApp":false},"sys":{}}

我要回帖

更多关于 mat分析内存泄漏 的文章

 

随机推荐