http://ttlnews.blogspot.com/2010/01/attacking-memory-problems-on-android.html

这篇文章是2010年1月份写的,其中有些已经不适合现在的Android机制了

我将内存问题分为两种:OOM和堆栈溢出 
一个Android进程可以分配的最大堆内存(heap memory)为 16M(现在各个定制版本的Android系统都不一样)

如果你将重复打开关闭一个Activity20次,就有可能出现内存溢出,那么在哪里内存溢出了,在哪里可以被GC回收?

adb shell procrank 
使用这个命令可以获得一些应用的内存数据,但是只能获取很简单的数据

adb shell dumpsys meminfo 
使用这个命令可以得到更多相关数据 

如果想要了解更多关于内存分配和回收的细节就需要 Eclipse Memory Analyzer Tool (MAT)工具。 
MAT工具使用方法:
 
MAT可以显示的内容
 
 
最佳实践:

1.在onDestroy()中最好将匿名listeners都设置为null,比如view.setOnClickListener(null) ;

2.即使没有静态drawable变量,但drawable对象中引用了view,view对象引用了Context,所以Activity可能会泄漏;

//Android源码片段   Drawable对象中引用View对象
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
public void setBackgroundDrawable(Drawable background) {
.....
background.setCallback(this);
......
}
} public abstract class Drawable {
public final void setCallback(Callback cb) {
mCallback = new WeakReference<Callback>(cb);
}
}

所以最好在 onDestroy() 中调用view.getBackground().setCallback(null); 
在stackoverflow的相关问答中出现了通用的unbindDrawables方法 
http://stackoverflow.com/questions/9461364/exception-in-unbinddrawables

@Override
protected void onDestroy()
{
super.onDestroy();
unbindDrawables(findViewById(R.id.top_layout));
System.gc();
Drawables(View view)
{
if (view.getBackground() != null)
{
view.getBackground().setCallback(null);
view.setBackground(null); //添加
}
if (view instanceof ViewGroup && !(view instanceof AdapterView))
{
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++)
{
unbindDrawables(((ViewGroup) view).getChildAt(i));
}
((ViewGroup) view).removeAllViews();
}
}
【补充】上述方法对ListView和GridView无用
 
3. 在onDestroy()中设置object = null 虽然设置object = null不是必要的,但是可以让GC加快一点;

4.让所有对象设置为null,虽然是很方便,但是也容易出错,最优的方法是通过MAT工具来监测哪些对象可能会泄漏,然后在 onDestroy()中置null。不然的话如果一个后台线程还在运行并且需要调用这些对象,那么就容易出现NullPointerExceptions。

5.Images(Bitmaps)不像一般Java对象一样分配内存而是通过调用natvie方法,所以bitmap占用了非Dalvik堆的内存。所以使用MAT工具时会发现用procrank和dumpsys命令查看的内存会比MAT中的大很多。 
【补充】 
 摘自https://developer.android.com/training/displaying-bitmaps/manage-memory.html (翻译:) http://su1216.iteye.com/blog/1931629  


(1) Android2.2及之前版本,GC时应用的线程都会停止,这样会引起延迟,导致性能降低。所以在2.3中添加了并发GC机制,所以只要bitmap不再被引用,所占内存就马上会被回收。

(2) 在2.3及之前版本,bitmap的像素数据是存储在native memory中的,并不是和bitmap对象一起存储在Dalvik heap里的,会导致native memory中的像素数据不能被显式回收,造成应用超出内存大小限制而崩溃。 
在2.3 (API level 11)之后的版本,像素数据就和bitmap对象一起存储在Dalvik heap上了。

6.加载网络如片在Android中会比较难以操作,只是加载几个200x200像素的图片就会导致内存占用的暴涨,20k大小的200x200的png图片需要占用160K左右的内存(每个像素占用4bytes)[200x200x4/1024 = 156.25k ]

推荐使用下列方法【摘自 http://android-developers.blogspot.com/2008/09/android-photostream.html】 
(1) 首先获得图片的的大小(像素)
BitmapFactory.Options options = new BitmapFactory.Options();
//只加载图片的部分信息,减少内存占用
options.inJustDecodeBounds = true;
Bitmap tmpBitmap = BitmapFactory.decodeStream(new ByteArrayInputStream(new URL(url).openStream()), null, options);
//获取图片的长宽像素
int height = options.outHeight;
int width = options.outWidth;

(2) 这样就在下载网络图片之前就可以知道图片的大小了,然后根据最终需要显示的图片大小进行压缩(通常Android会在绘制的时候缩放图片)

options = new BitmapFactory.Options();
options.inSampleSize = sampleSize;//压缩比列,如果是3,就会压缩到1/3
bitmap = BitmapFactory.decodeStream(new ByteArrayInputStream(new URL(url).openStream()), null, options);

这样获取的图片内存占用会小一点。

(3) 更好的办法是将图片下载到本地,在使用的时候根据大小重新缩放。
将不再需要图片对象时调用bitmap.recyce()来释放掉图片的内存占用,但是如果你调用了recycle(),之后又试图绘制这个bitmap,你会得到 错误:“Canvas: trying to use a recycled bitmap”(适合Android2.3及之前版本)。

7.ListView的convertView缓存方式 【网上资料很多】

8.debug的时候会保持对象处于可用状态,内存不能被回收。所以内存分析(使用MAT工具)的时候不要使用debug。并且在heap dump之前多进行几次GC操作

【原因:http://groups.google.com/group/android-developers/browse_thread/thread/7b0ea57d9507d33f

文章的回复里有个问题 
问题: 
我写了一个简单的demo,只有两个activity,重复打开SecondActivity 6次,使用MAT工具查看内存发现有6个SecondActivity对象,为什么会这样? 
回答: 
可能导致上述问题的原因 
(1) 怎么打开和关闭SecondActivity的,是通过Intent吗?是通过硬件返回按钮关闭SecondActivity的吗? 
(2) 在SecondActivity中重载onDestroy(),打上log,重复打开SecondActivity 6次,onDestroy()中的log会打印6次吗? 
(3) 用多快速度重新打开和关闭SecondActivity的?最好是当看见一个GC log时再重新打开,可能是还没有来得及GC,所以内存中会有多个对象(可有可能是你的demo内存占用太小,没有达到需要GC的条件) 
(4)检查一下6个SecondActivity对象的状态是否为unknown,MAT也会统计unkown状态的对象。【补充:可以使用adb shell dumpsys meminfo 命令查看当前内存中Activity存在的数目】 

Android内存泄漏监测(MAT)及解决办法的更多相关文章

  1. Android - 内存泄漏的情况以及解决方法

    [译]Android内存泄漏的八种可能(上) Android防止内存泄漏的八种方法(下). Static Activities 在类中定义了静态Activity变量,把当前运行的Activity实例赋 ...

  2. 前端知识体系:JavaScript基础-作用域和闭包-闭包的实现原理和作用以及堆栈溢出和内存泄漏原理和相应解决办法

    闭包的实现原理和作用 闭包: 有权访问另一个函数作用域中的变量的函数. 创建闭包的常见方式就是,在一个函数中创建另一个函数. 闭包的作用: 访问函数内部变量.保持函数在环境中一直存在,不会被垃圾回收机 ...

  3. Android 内存泄漏分析与解决方法

    在分析Android内存泄漏之前,先了解一下JAVA的一些知识 1. JAVA中的对象的创建 使用new指令生成对象时,堆内存将会为此开辟一份空间存放该对象 垃圾回收器回收非存活的对象,并释放对应的内 ...

  4. Android内存泄漏检測与MAT使用

    公司相关项目须要进行内存优化.所以整理了一些分析内存泄漏的知识以及工作分析过程. 本文中不会刻意的编写一个内存泄漏的程序,然后利用工具去分析它.而是通过介绍相关概念,来分析怎样寻找内存泄漏.并附上自己 ...

  5. LeakCanary 内存泄漏 监测 性能优化 简介 原理 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  6. Android内存泄漏检测利器:LeakCanary

    Android内存泄漏检测利器:LeakCanary MAR 28TH, 2016 是什么? 一言以蔽之:LeakCanary是一个傻瓜化并且可视化的内存泄露分析工具 为什么需要LeakCanary? ...

  7. Android内存泄漏的检测流程、捕捉以及分析

    https://blog.csdn.net/qq_20280683/article/details/77964208 Android内存泄漏的检测流程.捕捉以及分析 简述: 一个APP的性能,重度关乎 ...

  8. 利用 LeakCanary 来检查 Android 内存泄漏

    前言 你被概率性的 OOM 困扰么?有时候,OOM 像幽灵一样,挥之不去,可真想把它揪出来时,又捉之不着.或许,是时候用 LeakCanary 来诊断一下了.它是一个用来检查 Android 下内存泄 ...

  9. 【转】android 内存泄漏相关收藏博客。

    关于android内存泄漏的研究   博客建了几个月,都没有去写,一是因为当时换工作,然后又是新入职(你懂的,好好表现),比较忙:二是也因为自己没有写博客的习惯了.现在还算是比较稳定了,加上这个迭代基 ...

随机推荐

  1. openssl 非对称加密算法RSA命令详解

    1.非对称加密算法概述 非对称加密算法也称公开密钥算法,其解决了对称加密算法密钥分配的问题,非对称加密算法基本特点如下: 1.加密密钥和解密密钥不同 2.密钥对中的一个密钥可以公开 3.根据公开密钥很 ...

  2. html的空格显示距离问题

    一.使用全角空格 全角空格被解释为汉字,所以不会被被解释为HTML分隔符,可以按照实际的空格数显示. 二.使用空格的替代符号 替代符号就是在需要显示空格的地方加入替代符号,这些符号会被浏览器解释为空格 ...

  3. line-height下的dispay:inline-block垂直居中

    html: <div class="search fl"> <span class="search-box"> <input ty ...

  4. <c:if>标签

    <c:if>的用途就和我们一般在程序中用的if一样. 语法 语法1:没有本体内容(body) <c:if test="testCondition" var=&qu ...

  5. CSS定义网页滚动条

    (一)滚动条样式主要涉及到如下CSS属性: overflow属性: 检索或设置当对象的内容超过其指定高度及宽度时如何显示内容overflow: auto; 在需要时内容会自动添加滚动条overflow ...

  6. 从类的继承看socketserver源码

    当我们拿到一份python源代码,我们要怎么去看呢? 下面我们以socketserver为例,看下面的一段代码: #!/usr/bin/env python # -*- coding: UTF-8 - ...

  7. 递归:这帮坑爹的小兔崽子 - 零基础入门学习Python023

    递归:这帮坑爹的小兔崽子 让编程改变世界 Change the world by program 斐波那契数列的递归实现 这节课我们用斐波那契(Fibonacci)数列的递归实现来作为第一个例子吧,斐 ...

  8. gridview动态生成列

    // 有连接的列 if (!String.IsNullOrWhiteSpace(filedModel.C_SqlDetail)) { HyperLinkField hyperColumn = new ...

  9. IOS响应式编程框架ReactiveCocoa(RAC)使用示例-备

    ReactiveCocoa是响应式编程(FRP)在IOS中的一个实现框架,它的开源地址为:https://github.com/ReactiveCocoa/ReactiveCocoa# :在网上看了几 ...

  10. Oracle主键约束、唯一键约束、唯一索引的区别

    一般,我们看到术语“索引”和“键”交换使用,但实际上这两个是不同的.索引是存储在数据库中的一个物理结构,键纯粹是一个逻辑概念.键代表创建来实施业务规则的完整性约束.索引和键的混淆通常是由于数据库使用索 ...