android 检查内存泄漏内存能够泄漏多少

> 拉偶有所依的博客详情
内存泄漏简介
java可以保证当没有引用指向对象的时候,对象会被垃圾回收器回收,与c语言自己申请的内存自己释放相比,java程序员轻松了很多,但是并不代表java程序员不用担心内存泄漏。当java程序发生内存泄漏的时候往往具有隐蔽性。因此要借助一些去保证安全性,例如可以通过。
引用百度百科的定义:“用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元。直到程序结束”。从程序猿的角度来看“内存泄漏”,其实就是一个对象的生命周期超出了程序员所预期的长度(就叫它“该死不死”吧!),那么这个对象就泄漏了。
android开发中的内存泄漏
android应用程序本身系统分配的内存很少,一旦发生泄漏,程序很快就会变得非常卡顿,直至OOM崩溃。接下来将通过一个案例(只是为了分析内存泄漏而设计的玩具程序,切勿模仿)来介绍内存泄漏分析工具MAT,以及内存分析的技巧。
公欲善其事,先利其器
准备内存泄漏的分析工具,可以安装eclipse插件mat。如果eclise安装mat不成功,那可能是缺少必要的libs,如果嫌找库麻烦,可以只勾选第二项安装,不过会缺少某些功能,但是也够用了。&在线安装:&下载安装:
mat插件如何使用
如果已经成功安装好了mat工具,使用起来非常简单,首先将需要分析的应用程序跑起来,打开eclipse的devices视图你将会看到点击“Dump Hprof file”按钮,注意点击一下就可以了,然后等待(等待几秒)dump一个内存快照出来,接下来就会自动打开mat的视图了,如果mat没有安装成功,会让你保存一个.hprof文件到本地。看看下面的图例吧
dump hprof启动mat工具
人为制造一个内存泄漏
自定义一个ActivityManager,提供两个方法,分别用来注册与反注册Activity。
public class ActivityManager { & &private List&Activity& mActivities = new ArrayList&&(); & &private static ActivityManager sI & &private ActivityManager() { & &}; & &public static ActivityManager instance() { & & & &if (sInstance == null) { & & & & & &sInstance = new ActivityManager(); & & & &} & & & &return sI & &} & &public void registActivity(Activity activity) { & & & &mActivities.add(activity); & &} & &public void unRigistActivity(Activity activity) { & & & &mActivities.remove(activity); & &}}
在MainActivity的onCreate与onDestroy中分别调用registActivity和registActivity方法进行注册与反注册。但是OtherActivity却只是注册了,而忘记了反注册。
public class MainActivity extends Activity { & &public static final String TAG = MainActivity.class.getSimpleName(); & &private Button mB & &@Override & &protected void onCreate(Bundle savedInstanceState) { & & & &super.onCreate(savedInstanceState); & & & &setContentView(R.layout.activity_main); & & & &mBtn = (Button) findViewById(R.id.btn); & & & &mBtn.setOnClickListener(new OnClickListener() { & & & & & &@Override & & & & & &public void onClick(View v) { & & & & & & & &Intent intent = new Intent(); & & & & & & & &intent.setClass(MainActivity.this, OtherActivity.class); & & & & & & & &startActivity(intent); & & & & & &} & & & &}); & & & & & & & &ActivityManager.instance().registActivity(this); & &} & &@Override & &protected void onDestroy() { & & & &super.onDestroy(); & & & & & & & &ActivityManager.instance().registActivity(this); & &}public class OtherActivity extends Activity { & &public static final String TAG = OtherActivity.class.getSimpleName(); & &private Object[] mObjs = new Object[10000];//模拟快速消耗内存,使效果明显 & &private Button mB & &@Override & &protected void onCreate(Bundle savedInstanceState) { & & & &super.onCreate(savedInstanceState); & & & &setContentView(R.layout.activity_other); & & & &mBtn = (Button) findViewById(R.id.btn); & & & &mBtn.setOnClickListener(new OnClickListener() { & & & & & &@Override & & & & & &public void onClick(View v) { & & & & & & & &for (int i = 0; i & mObjs. i++) { & & & & & & & & & &mObjs[i] = new Object(); & & & & & & & &} & & & & & & & & & & & & & & & &finish(); & & & & & &} & & & &}); & & & &ActivityManager.instance().registActivity(this); & &} & &@Override & &protected void onDestroy() { & & & &super.onDestroy(); & &}}
案例中的内存泄漏是人为构造的,所以我们事先已经知道有泄漏了,但是实际的开发过程中,内存泄漏是隐蔽的,一开始我们并不知道,所以我们需要通过一些手段来测试APP是否有内存泄漏。首先在Devices视图中选中需要测试的进程,然后点击Device视图面板的Update Heap按钮,然后打开Heap视图,点击Cause GC。然后反复的在MainActivyt和OtherActivity之间切换。观察Heap size的变化。你会发现内存一直在增加。没有稳定下来的趋势。这个时候你就有理由怀疑内存泄漏了。
Update heap观察heap size等变化情况
找出泄漏的对象
按照前面mat的使用步骤,dump一个内存快照出来,然后从分析报告中点击“Leak suspects”这里会列车可能泄漏的对象,其中你会发现“&com.vjson.leaks.OtherActivity”的身影。OtherActivity这个类有33个实例,作为代码的生产者,你应该一下子就会发现,原来是OtherActivity泄漏了。发现它泄漏之后,如何找出是哪一个对象持有了OtherActivity对象的引用呢?
可能泄漏的报告
找出引用链
使用OQL对象查询语言查询出泄漏的对象,写过SQL的同学一定对她有一种既陌生又熟悉的感觉,和SQL非常相似,语法简单易懂,但是非常强大select *from com.vjson.leaks.OtherActivity赛选出OtherActivity这一类对象,然后选择“exclude weak/soft references”赛选出除了软引用和弱引用之外的对象,也就是强引用了!。对象的引用类型不在本文的讲解范围,但是你一定要知道“强引用”,“软引用”,“弱引用”。“幽灵引用”,如果不知道自行脑补去吧!
OQL对象查询找出引用链
对象引用链
然后找出GC的根节点,从图二种可以看出,原来Activity对象被ActivityManager里面的ArrayList给hold住了,所以接下来的工作就是在OtherActivity的onDestroy中反注册,内存泄漏就被解决了。
Android开发中常见的内存泄漏
对象没有反注册
数据库cursor没有关闭
Bitmap没有回收
ListView item没有复用
Handler在Activity中定义为非static的匿名内部类
如果耐心的看完本文,那么恭喜你妈妈再也不用担心内存泄漏了。其实只要掌握了分析问题的技巧与工具,内存泄漏so easy。文章中只是简单的介绍了工具与技巧,这其中还有很多技巧需要自己去摸索。&
人打赏支持
码字总数 138944
支付宝支付
微信扫码支付
打赏金额: ¥
已支付成功
打赏金额: ¥
& 开源中国(OSChina.NET) |
开源中国社区(OSChina.net)是工信部
指定的官方社区中国领先的IT技术网站
51CTO旗下网站
Android内存泄漏的八种可能
Java是垃圾回收语言的一种,其优点是开发者无需特意管理内存分配,降低了应用由于局部故障(segmentation fault)导致崩溃,同时防止未释放的内存把堆栈(heap)挤爆的可能,所以写出来的代码更为安全。
作者:豆沙包lo来源:| 14:09
Java是垃圾回收语言的一种,其优点是开发者无需特意管理内存分配,降低了应用由于局部故障(segmentation fault)导致崩溃,同时防止未释放的内存把堆栈(heap)挤爆的可能,所以写出来的代码更为安全。
不幸的是,在Java中仍存在很多容易导致内存泄漏的逻辑可能(logical leak)。如果不小心,你的Android应用很容易浪费掉未释放的内存,最终导致内存用光的错误抛出(out-of-memory,OOM)。
一般内存泄漏(traditional memory leak)的原因是:当该对象的所有引用都已经释放了,对象仍未被释放。(译者注:Cursor忘记关闭等)
逻辑内存泄漏(logical memory leak)的原因是:当应用不再需要这个对象,当仍未释放该对象的所有引用。
如果持有对象的强引用,垃圾回收器是无法在内存中回收这个对象。
在Android开发中,最容易引发的内存泄漏问题的是Context。比如Activity的Context,就包含大量的内存引用,例如View Hierarchies和其他资源。一旦泄漏了Context,也意味泄漏它指向的所有对象。Android机器内存有限,太多的内存泄漏容易导致OOM。
检测逻辑内存泄漏需要主观判断,特别是对象的生命周期并不清晰。幸运的是,Activity有着明确的生命周期,很容易发现泄漏的原因。Activity.onDestroy()被视为Activity生命的结束,程序上来看,它应该被销毁了,或者Android系统需要回收这些内存(译者注:当内存不够时,Android会回收看不见的Activity)。
如果这个方法执行完,在堆栈中仍存在持有该Activity的强引用,垃圾回收器就无法把它标记成已回收的内存,而我们本来目的就是要回收它!
结果就是Activity存活在它的生命周期之外。
Activity是重量级对象,应该让Android系统来处理它。然而,逻辑内存泄漏总是在不经意间发生。(译者注:曾经试过一个Activity导致20M内存泄漏)。在Android中,导致潜在内存泄漏的陷阱不外乎两种:
全局进程(process-global)的static变量。这个无视应用的状态,持有Activity的强引用的怪物。
活在Activity生命周期之外的线程。没有清空对Activity的强引用。
检查一下你有没有遇到下列的情况。
Static Activities
在类中定义了静态Activity变量,把当前运行的Activity实例赋值于这个静态变量。
如果这个静态变量在Activity生命周期结束后没有清空,就导致内存泄漏。因为static变量是贯穿这个应用的生命周期的,所以被泄漏的Activity就会一直存在于应用的进程中,不会被垃圾回收器回收。
static&Activity&&&&&&&void&setStaticActivity()&{&&&&&&&activity&=&this;&&&&&}&&&&&&View&saButton&=&findViewById(R.id.sa_button);&&&&&saButton.setOnClickListener(new&View.OnClickListener()&{&&&&&&&@Override&public&void&onClick(View&v)&{&&&&&&&&&setStaticActivity();&&&&&&&&&nextActivity();&&&&&&&}&&&&&});&
Memory Leak 1 - Static Activity
Static Views
类似的情况会发生在单例模式中,如果Activity经常被用到,那么在内存中保存一个实例是很实用的。正如之前所述,强制延长Activity的生命周期是相当危险而且不必要的,无论如何都不能这样做。
特殊情况:如果一个View初始化耗费大量资源,而且在一个Activity生命周期内保持不变,那可以把它变成static,加载到视图树上(View Hierachy),像这样,当Activity被销毁时,应当释放资源。(译者注:示例代码中并没有释放内存,把这个static view置null即可,但是还是不建议用这个static view的方法)
static&&&&&&&void&setStaticView()&{&&&&&&&view&=&findViewById(R.id.sv_button);&&&&&}&&&&&&View&svButton&=&findViewById(R.id.sv_button);&&&&&svButton.setOnClickListener(new&View.OnClickListener()&{&&&&&&&@Override&public&void&onClick(View&v)&{&&&&&&&&&setStaticView();&&&&&&&&&nextActivity();&&&&&&&}&&&&&});&
Memory Leak 2 - Static View
Inner Classes
继续,假设Activity中有个内部类,这样做可以提高可读性和封装性。将如我们创建一个内部类,而且持有一个静态变量的引用,恭喜,内存泄漏就离你不远了(译者注:销毁的时候置空,嗯)。
private&static&Object&&&&&&&&&&void&createInnerClass()&{&&&&&&&&&class&InnerClass&{&&&&&&&&&}&&&&&&&&&inner&=&new&InnerClass();&&&&&}&&&&&&View&icButton&=&findViewById(R.id.ic_button);&&&&&icButton.setOnClickListener(new&View.OnClickListener()&{&&&&&&&&&@Override&public&void&onClick(View&v)&{&&&&&&&&&&&&&createInnerClass();&&&&&&&&&&&&&nextActivity();&&&&&&&&&}&&&&&});&
Memory Leak 3 - Inner Class
内部类的优势之一就是可以访问外部类,不幸的是,导致内存泄漏的原因,就是内部类持有外部类实例的强引用。
Anonymous Classes
相似地,匿名类也维护了外部类的引用。所以内存泄漏很容易发生,当你在Activity中定义了匿名的AsyncTsk
。当异步任务在后台执行耗时任务期间,Activity不幸被销毁了(译者注:用户退出,系统回收),这个被AsyncTask持有的Activity实例就不会被垃圾回收器回收,直到异步任务结束。
void&startAsyncTask()&{&&&&&&&&&new&AsyncTask&Void,&Void,&Void&()&{&&&&&&&&&&&&&@Override&protected&Void&doInBackground(Void...&params)&{&&&&&&&&&&&&&&&&&while(true);&&&&&&&&&&&&&}&&&&&&&&&}.execute();&&&&&}&&&&&&super.onCreate(savedInstanceState);&&&&&setContentView(R.layout.activity_main);&&&&&View&aicButton&=&findViewById(R.id.at_button);&&&&&aicButton.setOnClickListener(new&View.OnClickListener()&{&&&&&&&&&@Override&public&void&onClick(View&v)&{&&&&&&&&&&&&&startAsyncTask();&&&&&&&&&&&&&nextActivity();&&&&&&&&&}&&&&&});&
Memory Leak 4 - AsyncTask
同样道理,定义匿名的Runnable,用匿名类Handler执行。Runnable内部类会持有外部类的隐式引用,被传递到Handler的消息队列MessageQueue中,在Message消息没有被处理之前,Activity实例不会被销毁了,于是导致内存泄漏。
void&createHandler()&{&&&&&&&&&new&Handler()&{&&&&&&&&&&&&&@Override&public&void&handleMessage(Message&message)&{&&&&&&&&&&&&&&&&&super.handleMessage(message);&&&&&&&&&&&&&}&&&&&&&&&}.postDelayed(new&Runnable()&{&&&&&&&&&&&&&@Override&public&void&run()&{&&&&&&&&&&&&&&&&&while(true);&&&&&&&&&&&&&}&&&&&&&&&},&Long.MAX_VALUE&&&&1);&&&&&}&&&&&&&View&hButton&=&findViewById(R.id.h_button);&&&&&hButton.setOnClickListener(new&View.OnClickListener()&{&&&&&&&&&@Override&public&void&onClick(View&v)&{&&&&&&&&&&&&&createHandler();&&&&&&&&&&&&&nextActivity();&&&&&&&&&}&&&&&});&
Memory Leak 5 - Handler
我们再次通过Thread和TimerTask来展现内存泄漏。
void&spawnThread()&{&&&&&&&&&new&Thread()&{&&&&&&&&&&&&&@Override&public&void&run()&{&&&&&&&&&&&&&&&&&while(true);&&&&&&&&&&&&&}&&&&&&&&&}.start();&&&&&}&&&&&&View&tButton&=&findViewById(R.id.t_button);&&&&&tButton.setOnClickListener(new&View.OnClickListener()&{&&&&&&&@Override&public&void&onClick(View&v)&{&&&&&&&&&&&spawnThread();&&&&&&&&&&&nextActivity();&&&&&&&}&&&&&});&
Memory Leak 6 - Thread
只要是匿名类的实例,不管是不是在工作线程,都会持有Activity的引用,导致内存泄漏。
oid&scheduleTimer()&{&&&&&&&&&new&Timer().schedule(new&TimerTask()&{&&&&&&&&&&&&&@Override&&&&&&&&&&&&&public&void&run()&{&&&&&&&&&&&&&&&&&while(true);&&&&&&&&&&&&&}&&&&&&&&&},&Long.MAX_VALUE&&&&1);&&&&&}&&&&&&View&ttButton&=&findViewById(R.id.tt_button);&&&&&ttButton.setOnClickListener(new&View.OnClickListener()&{&&&&&&&&&@Override&public&void&onClick(View&v)&{&&&&&&&&&&&&&scheduleTimer();&&&&&&&&&&&&&nextActivity();&&&&&&&&&}&&&&&});&
Memory Leak 7 - TimerTask
Sensor Manager
最后,通过Context.getSystemService(int name)可以获取系统服务。这些服务工作在各自的进程中,帮助应用处理后台任务,处理硬件交互。如果需要使用这些服务,可以注册监听器,这会导致服务持有了Context的引用,如果在Activity销毁的时候没有注销这些监听器,会导致内存泄漏。
void&registerListener()&{&&&&&&&&&&&&&&&&SensorManager&sensorManager&=&(SensorManager)&getSystemService(SENSOR_SERVICE);&&&&&&&&&&&&&&&&Sensor&sensor&=&sensorManager.getDefaultSensor(Sensor.TYPE_ALL);&&&&&&&&&&&&&&&&sensorManager.registerListener(this,&sensor,&SensorManager.SENSOR_DELAY_FASTEST);&&&&&&&&&}&&&&&&&&&&View&smButton&=&findViewById(R.id.sm_button);&&&&&&&&&smButton.setOnClickListener(new&View.OnClickListener()&{&&&&&&&&&&&&&@Override&public&void&onClick(View&v)&{&&&&&&&&&&&&&&&&&registerListener();&&&&&&&&&&&&&&&&&nextActivity();&&&&&&&&&&&&&}&&&&&&&&&});&
Memory Leak 8 - Sensor Manager
看过那么多会导致内存泄漏的例子,容易导致吃光手机的内存使垃圾回收处理更为频发,甚至最坏的情况会导致OOM。垃圾回收的操作是很昂贵的开销,会导致肉眼可见的卡顿。所以,实例化的时候注意持有的引用链,并经常进行内存泄漏检查。
原 文: 【编辑推荐】【责任编辑: TEL:(010)】
大家都在看猜你喜欢
热点原创外电热点热点
24H热文一周话题本月最赞
讲师:1人学习过
讲师:1人学习过
讲师:1人学习过
精选博文论坛热帖下载排行
本书是一本介绍当前主流计算机网络应用技术的工具图书,全面总结了当前最主流、最基础的计算机网络应用,包括局域网和互联网应用两方面。在...
订阅51CTO邮刊[Android] 内存泄漏调试经验分享 (二) - 美帝亡我之心不死,腐败终成抽心一烂。 - ITeye技术网站
博客分类:
接上篇:[Android] 内存泄漏调试经验分享 (一)
三、内存监测工具
无论怎么小心,想完全避免是不可能的,此时就需要一些工具来帮助我们检查代码中是否存在会造成内存泄漏的地方。中的就带有一个很不错的内存监测工具这里我使用的插件,并以真机为例,在模拟器中的情况类似。用监测应用进程使用内存情况的步骤如下:
1. 启动后,切换到透视图,并确认视图、视图都是打开的;
2. 将手机通过链接至电脑,链接时需要确认手机是处于“调试”模式,而不是作为“”;
3. 链接成功后,在的视图中将会显示手机设备的序列号,以及设备中正在运行的部分进程信息;
4. 点击选中想要监测的进程,比如进程;
5. 点击选中视图界面中最上方一排图标中的“”图标;
6. 点击视图中的“”按钮;
7. 此时在视图中就会看到当前选中的进程的内存使用量的详细情况[如图所示]。
a) 点击“”按钮相当于向虚拟机请求了一次操作;
b) 当内存使用信息第一次显示以后,无须再不断的点击“”,视图界面会定时刷新,在对应用的不断的操作过程中就可以看到内存使用的变化;
c) 内存使用信息的各项参数根据名称即可知道其意思,在此不再赘述。
如何才能知道我们的程序是否有内存泄漏的可能性呢。这里需要注意一个值:视图中部有一个叫做,即数据对象,也就是我们的程序中大量存在的类类型的对象。在一行中有一列是“”,其值就是当前进程中所有数据对象的内存总量,一般情况下,这个值的大小决定了是否会有内存泄漏。可以这样判断:
a) 不断的操作当前应用,同时注意观察的值;
b) 正常情况下值都会稳定在一个有限的范围内,也就是说由于程序中的的代码良好,没有造成对象不被垃圾回收的情况,所以说虽然我们不断的操作会不断的生成很多对象,而在虚拟机不断的进行的过程中,这些对象都被回收了,内存占用量会会落到一个稳定的水平;
c) 反之如果代码中存在没有释放对象引用的情况,则的值在每次后不会有明显的回落,随着操作次数的增多的值会越来越大,
直到到达一个上限后导致进程被掉。
d) 此处已进程为例,在我的测试环境中进程所占用的内存的的正常情况下会稳定在之间,而当其值超过后进程就会被。
总之,使用的视图工具可以很方便的确认我们的程序是否存在内存泄漏的可能性。
四、内存分析工具
如果使用确实发现了我们的程序中存在内存泄漏,那又如何定位到具体出现问题的代码片段,最终找到问题所在呢?如果从头到尾的分析代码逻辑,那肯定会把人逼疯,特别是在维护别人写的代码的时候。这里介绍一个极好的内存分析工具 。
MAT是一个插件,同时也有单独的客户端。官方下载地址、介绍和详细的使用教程请参见:,在此不进行说明了。另外在安装后的帮助文档里也有完备的使用教程。在此仅举例说明其使用方法。我自己使用的是的插件,使用插件要比稍微方便一些。
使用进行内存分析需要几个步骤,包括:生成文件、打开并导入文件、使用的视图工具分析内存。以下详细介绍。
(一) 生成.hprof文件
生成文件的方法有很多,而且的不同版本中生成的方式也稍有差别,我使用的版本的是,各个版本中生成文件的方法请参考:
http://android.git.kernel.org/?p=platform/dalvik.a=blob_f=docs/heap-profiling.hb=HEAD。
1. 打开并切换到透视图,同时确认、和视图已经打开了;
2. 将手机设备链接到电脑,并确保使用“调试”模式链接,而不是““模式;
3. 链接成功后在视图中就会看到设备的序列号,和设备中正在运行的部分进程;
4. 点击选中想要分析的应用的进程,在视图上方的一行图标按钮中,同时选中“”和“”两个按钮;
5. 这是工具将会自动生成当前选中进程的文件,并将其进行转换后存放在当中,如果你已经安装了插件,那么此时将会自动被启用,并开始对文件进行分析;
注意:第步和第步能够正常使用前提是我们需要有,并且当前进程有向中写入的权限,否则文件不会被生成,在中会显示诸如
ERROR/dalvikvm(8574): hprof: can't open /sdcard/com.xxx.hprof-hptemp: Permission denied.
如果我们没有,或者当前进程没有向写入的权限(如),那我们可以这样做:
6. 在当前程序中,例如中某些代码中,可以使用中的:
public static void dumpHprofData(String fileName) throws IOException
方法,手动的指定文件的生成位置。例如:
xxxButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
android.os.Debug.dumpHprofData("/data/temp/myapp.hprof");
上述代码意图是希望在被点击的时候开始抓取内存使用信息,并保存在我们指定的位置:,这样就没有权限的限制了,而且也无须用。但要保证目录是存在的。这个路径可以自己定义,当然也可以写成当中的某个路径。
(二) 使用MAT导入.hprof文件
1. 如果是自动生成的文件,可以使用插件直接打开(可能是比较新的才支持);
2. 如果自动生成的文件不能被直接打开,或者是使用方法手动生成的文件,则需要将文件进行转换,转换的方法:
例如我将文件拷贝到上的目录下,并输入命令,其中为原始文件,为转换过后的文件。转换过后的文件自动放在目录下。,到此为止,文件处理完毕,可以用来分析内存泄露情况了。
3. 在中点击,或者打的。在中点击,浏览并导入刚刚转换而得到的文件。
(三) 使用MAT的视图工具分析内存
导入文件以后,会自动解析并生成报告,点击,并按分组,选择自己所定义的类点右键,在弹出菜单中选择。这时会列出所有可疑类,右键点击某一项,并选择,会进一步筛选出跟程序相关的所有有内存泄露的类。据此,可以追踪到代码中的某一个产生泄露的类。
MAT的界面如下图所示。
具体的分析方法在此不做说明了,因为在的官方网站和客户端的帮助文档中有十分详尽的介绍。
了解中各个视图的作用很重要,例如中介绍的。
总之使用分析内存查找内存泄漏的根本思路,就是找到哪个类的对象的引用没有被释放,找到没有被释放的原因,也就可以很容易定位代码中的哪些片段的逻辑有问题了。
至此请各位自己动手,丰衣足食吧!
&!--EndFragment--&
浏览 25425
你好, 我在代码里加入了下面这句,但一直报错,未找到原因,请问是什么原因,谢谢!建议检查一下log,“check log output for details”,log中是否直接输出了原因。应该是说你没权限。1. 检查一下你的应用是否申请了SD卡的写权限2. 换个目录输出,比如:/sdcard/temp/dump.hprof
浏览: 295020 次
来自: 北京
summertime2013 写道最后一句话,为什么继续超过1 ...
真心牛人啊
最后一句话,为什么继续超过10秒没返回,又会引起第2种ANR? ...
楼主,求出书啊。求内容啊
读了您的几篇文章,找到了很多昨天上午向您请教问题的答案,受益匪 ...检测和解决Android应用的性能问题
发表于 09:50|
来源code.tutsplus|
作者Jessica Thornsby
摘要:开发者在忙碌于构建精美的用户界面或完成新的特性时,很容易忘记性能相关的事情。本文作者就解决Android应用性能问题给出了三个方面,即过度绘制、Android图形渲染以及Memory Leaks内存泄漏。
3. Memory Leaks 内存泄漏
步骤1:问题描述
Android是一个自动管理内存的开发环境,不要让这个句话蒙蔽了,因为内存泄漏依旧是可能发生的。这是因为垃圾回收器只会移除那些不可达的对象。如果它不是一个不可达的对象,那么该对象就不会被释放掉。
这些不可达的对象阴魂不散,聚集在你的堆内存中,占用App的内存控件。如果你继续泄漏对象,那么可用的内存空间将会越来越小,GC操作就会频繁触发。
有两个原因表明这是一个坏消息。首先,GC操作通常不会明显地影响你的App性能,但是当内存控件较小时大量的GC操作会使你的App变慢,此时UI就不会那么流畅了。第二问题是移动设备的内存空间相对来说较小,因此内存泄漏会快速地升级为内存溢出,导致应用Crash。
内存泄漏难以被检测出。可能只有当用户开始抱怨你的应用时你才能发觉内存泄漏问题。幸运地是,Android SDK提供了一些有用的工具来让你找到这些问题。(译者注
: Square的开源库LeakCanary是查找内存泄漏的优秀工具,强烈建议大家使用)。
步骤2 : 内存监视器 (Memory Monitor)
Memory Monitor是一个能够实时获取应用内存使用情况的工具。需要注意的是这个工具只能作用于正在运行的应用,因此确保你的要测试的应用已经安装到你的设备中,并且你的设备已经连接到你的电脑上。
Memory Monitor已经内置在Android Studio中,因此你可以点击Android Studio的底部的”Memory”这个tab来切换到内存监视页面。当你切换到该页面的时候,Memory
Monitor就开始记录你的内存使用情况了。
如果Memory Monitor没有开始记录,那么确保你的设备是已经被选中的状态。
如果Memory Monitor提示No debuggable applications,那么你可以打开Android Studio的”Tools”菜单,选择”Android”,然后确保选中了Enable
adb integration。这个功能还不是很稳定,所以有时候你需要手动切换它的状态。你也可以断开设备与电脑的连接,然后再重连,这样可能就OK了。
一旦Memory Monitor检测到正在运行的应用,它就会显示这个应用的内存使用情况。已使用的内存会被表示为深蓝色,未分配的内存则会变为浅蓝色。
花一些时间与你的设备交互,并且关注你的内存使用情况。最终已分配的内存会增长,直到没有内存可用。此时,系统就会释放触发GC释放内存,当你看到已分配的内存明显的下降时就代表GC操作被触发了。
GC通常情况下会将无用的内存释放掉,但是当你看到App在短时间内快速增长或者GC变得非常频繁,此时你就需要倍加小心了,这就是发生内存泄漏的信号!
如果你通过Memory Monitor来追踪一个可疑的内存泄漏问题,你可能会看到Android系统会为你的App增大可用内存,TODO :
最终,你可能会看到你的App消耗了非常多的内存以至于系统无法再给你的应用更多的可用内存。如果你看到这种场景,那么说明你在内存使用上犯了很严重的错误。
步骤3 : Android Device Monitor
另一个能够帮助你收集更新关于内存泄漏信息和其他内存相关问题的工具是Android Device Monitor的DDMMS工具下的Heap。
Heap工具能够通过显示系统为你分配了多少内存来帮助你诊断内存泄漏问题。正如上面提到的,如果已分配的内存不断地增长,那么这是发生内存泄漏的明显信号。
但是这个工具还提供了许多关于你的应用堆内存使用情况的数据,包含你的App内分配的各种对象、分配的对象数量以及这些对象占用了多少空间。这些额外的信息对于你追踪内存泄漏极为有用。
你可以在Android Device Monitor工具中选择DDMS,在Devices中选择你要检测的App。然后选择Heap标签,如图所示。然后花一些时间与你的App进行交互以收集内存信息。
heap输出信息会在GC事件之后,因为你可以手动点击Cause GC来触发GC,使得Heap内存数据尽快地显示出来。
一旦GC事件被触发了,heap标签下就会更新App的堆内存使用信息,这些信息会在每次GC时更新。
在这篇文章中,我们学习了一些开发中最常见的性能问题,过度绘制、内存泄漏、缓慢的UI渲染。
相信你已经掌握了如何使用工具来检查这些问题,以及如何获取更新的信息来判断你的应用中是否出现了这些性能问题。你有越多的信息,就越容易追踪到问题的原因并且修复它。
Android SDK有很多工具可以供你诊断和定位性能问题。如果你想学习更多这方面的知识,你可以访问u这两篇官方文档和。
文章来源:,译文出自:。译者简介:何红辉(Mr.Simple),资深Android工程师,开发技术前线项目站长,现就职于友盟,负责友盟社会化组件、微社区的研发工作。
第一时间掌握最新移动开发相关信息和技术,请关注mobilehub公众微信号(ID: mobilehub)。&
推荐阅读相关主题:
CSDN官方微信
扫描二维码,向CSDN吐槽
微信号:CSDNnews
相关热门文章

我要回帖

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

 

随机推荐