memory analyzer (内存分析)
android 内存优化关键点
(一:布局)
- 背景图的选择:
- 对于布局中的背景,能用color的尽量使用color,不要是图片代替。
- 对于规则的图片,能用shape画图的,尽量使用shape,不要使用图片
- 复用ItemView:
- 对于ListView、GridView、RecycleView可以使用item复用技术。
(二:变量声明)
- 少使用static修饰词:
- 内存分配:使用static修饰的对象只进行一次内存分配,假如有多
个实例,则共享这个对象。 - 生命周期:static修饰的变量的在编译后所分配的内存会一直存在,因此可以直接通
过类名.进行访问,程序消亡时,内存释放。 - 优化策略: 由于static修饰的生命周期和程序的生命周期同样长,因此,如果
程序中过多的时候static修饰小的对象,或者修饰内存开销比较大的对象,就会造成内存
过高使用,容易引发OOM.
- 内存分配:使用static修饰的对象只进行一次内存分配,假如有多
- 字符串操作:
- 原因: java中对String的处理极为特殊,它可以不需要使用new关键字而直接
创建对象,分配内存,如果在大量的字符串拼接时使用String,就会造成大量的内存分
配,造成内存浪费 - 解决: 尽量使用StringBuffer提高效率。
- 原因: java中对String的处理极为特殊,它可以不需要使用new关键字而直接
(三:引用的传递)
- 弱化无关引用:
- 大多数情况下我们需要传递引用,但是无法确保被传递出去的引用在何时销毁,就会
造成内存泄漏。如果知道哪些为无关引用,则可以将其设置为null,GC会自动回收。
- 大多数情况下我们需要传递引用,但是无法确保被传递出去的引用在何时销毁,就会
- 善用SoftReference/WeakReference
- SoftReference:当内存吃紧的时候,系统会自动释放使用SoftReference包装的
对象。 - WeakReference:如果对象消耗内存较大,那么对象使用完就需要释放,避免造成
内存溢出,则可以使用WeakReference,当GC扫过那片内存或者内存吃紧时会被收回。
- SoftReference:当内存吃紧的时候,系统会自动释放使用SoftReference包装的
(四:未及时关闭造成的内存泄漏)
- Cursor、I/O流、BroadCastReceiver、Service、观察者模式等
- 这五种需要及时关闭或解绑。
(五:handler)
谨慎使用handler:
- 泄漏原因: 在处理异步操作的时候,handler+thread是个不错的选择。但是
handle运行于主线程,负责处理MessageQueue的消息,但是当handler还有消息需要
处理或者线程未执行完,activity页面已经结束的情况下,该activity的引用并不会
被回收,这就造成了内存泄漏。 解决方案:
- 在onDestroy方法中关闭线程。
- 在onDestroy方法中调用
handler.removeCallbacksAndMessages(null)
取消所有的消息处理。 - 声明handler的内部类为static,需要结合WeakReference使用,因为static
内部类不能持有外部类的引用,所以需要把外部类以弱引用的形式传递进来。
public class Main2Activity extends AppCompatActivity { private static class myHandler extends Handler { private WeakReference<Main2Activity> mActivity; public myHandler(Main2Activity activity) { mActivity = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); Main2Activity activity = mActivity.get(); if (activity != null) { //to do something Log.i("aaa", "handler something..."); } } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); final myHandler myHandler = new myHandler(this); myHandler.postDelayed(new Runnable() { @Override public void run() { // to do something Log.i("aaa", "Runnable"); myHandler.sendEmptyMessage(1); } }, 1000 * 6); } }
- 泄漏原因: 在处理异步操作的时候,handler+thread是个不错的选择。但是
(六:Bitmap)
- 谨慎处理Bitmap:
- 法则一:使用完及时释放
recycle()
。 - 法则二:可适当放缩,通过
BitmapFactory.Options.inSampleSize
属性控制。 - 法则三:避免根据像素去分配内存,如果只需要获取Bitmap的属性,可以使用
Bit
属性。
mapFactory.Options.inJustDecodeBounds - 法则四:在加载网络图片时,尽量使用SoftReference或者WeakReference并进行
本地缓存。 - 法则五:使用大牛的框架。
- 法则一:使用完及时释放
(七:线程)
- 线程操作:
- 及时关闭
- 控制数据
- 使用线程池
(八:activity中的耗时操作)
耗时的非静态内部类
- 原因: 在activity中,非静态内部类持有activity的引用,如果activity
已经关闭,而内部类的耗时操作仍在继续,那么activity的引用将无法销毁,造成内存
泄漏。比如说线程,或者网络请求等。
- 原因: 在activity中,非静态内部类持有activity的引用,如果activity
去除不必要的抽象:
- 抽象的成本很高,执行他们需要更多的代码,需要更多的时间和更多的RAM来将代码
映射到内存中,因此需要去除没有必要的抽象。
- 抽象的成本很高,执行他们需要更多的代码,需要更多的时间和更多的RAM来将代码
谨慎使用service
(九:数据结构相关)
- SparseArray和ArrayMap替代HashMap
- HashMap原理
- SparseArray原理
- ArrayMap原理
- 参考:https://blog.csdn.net/u010687392/article/details/47809295
参考:
https://blog.csdn.net/tuke_tuke/article/details/52316285
内存分析工具
MAT(Memory Analyzer Tools)
获取Dump文件既堆转储文件:格式为hprof
1.打开android studio 运行你的程序,点击Tools->Android->Android Device
Monitor->选中DDMS,选中你的程序,选中左边的Dump HPROF File ,保存生成的文件转换Dump文件:
- 工具所在位置:通过DDMS获取的.hprof文件需要转换才能被MAT识别,android SDK
中有hprof-conv.exe的程序,可以转换这个文件,位置: Sdk\platform-tools
下。 - 转换:在Sdk\platform-tools目录下打开CMD,输入命令:
hprof-conv xxx.hprof yyy.hprof
,即可转换文件,
注意: 从DDMS获取的.hprof文件一定要放在Sdk\platform-tools目录下。
- 工具所在位置:通过DDMS获取的.hprof文件需要转换才能被MAT识别,android SDK
分析转换后的文件,格式仍为hprof
导入hprof文件以后会出现两个界面,一个是Overview(概览),一个是default
reports(默认报告)。- Overview:展示的是这个Dump文件一个总体信息,通过这个圆饼图可以查看各个
部分所占用的内存情况。圆饼图下面分别是actions、reports和step by step,
actions 中包含了四个比较重要的功能,最常用的是Histogram
- Histogram: 它按类名将所有的实例对象列出来,表头可以使用正则表
达式匹配。
- 此处有两个Main2Activity,说明有内存泄漏,鼠标右键点击,选择list
objects->with incoming references,点开可以查看是谁持有Main2Activity
对象的引用造成了泄漏。例如本例是(为了测试故意使用handler来造成内存泄漏)
:handler,泄漏原因和所在线程都显示出来了
- 快速查找: 右键点击->Path to GC roots->exclude all
phantom/weak/soft etc. reference(排除所有的虚、弱、软引用)
- Overview:展示的是这个Dump文件一个总体信息,通过这个圆饼图可以查看各个
对比查找原因:
参考:
https://blog.csdn.net/aaa2832/article/details/19419679
基础
java中内存是什么分配的?(Think in java)
- 寄存器:这是最快的存储区,位于处理器内部。寄存器极其有限,根据需求分配,java无法
控制,但C、C++可以建议建议寄存器的分配方式。 堆栈:速度仅次于寄存器,位于RAM(随机访问存储器)。堆栈可以通过堆栈指针从处理器
那里获得直接的支持。堆栈指针上移释放内存(可以想象activity的栈),堆栈指针下移分配
内存。程序创建时,java必须知道存储在堆栈中的所有项的确切的生命周期,以便上移下移指
针,所以这就限制了程序的灵活性。java中对象的引用存储在堆栈中,但是java对象本身并
不在堆栈中。优点: 速度快
缺点: 需要知道确切的生命周期才能分配内存堆:通用的存储区,也位于RAM中。堆不需要知道存储数据在堆中的存活的时间,因此灵活
性更强。使用时只需要new一个对象,堆就会自动进行内存分配,但是付出的代价是:进行内存
的分配和清理要发费更长的时间。java中所有的对象都存储在堆中。优点: 灵活
缺点: 速度慢常量池:存放代码中的常量。
非RAM存储:如果有些数据完全存活于程序之外,那么他们可以不受程序控制。比如:流对
象和持有话对象。
java中基本数据类型的分配
- java中通常new一个很小的基本数据类型在堆中往往不是很有效,因此对于基本数据类型,
java通常不用new来创建变量,而是创建一个非引用的变量,这个变量直接存储值,并置于堆栈
中,因此更加高效。 - java中每种基本类型都对应有封装类型,封装类型可以在堆中创建一个非基本对象,用来
表示对应的基本数据类型。
强引用、弱引用
https://blog.csdn.net/mazhimazh/article/details/19752475
Garbage Collection(GC)
- Garbage Collection回收的是哪部分内存:负责回收堆(heap)内存中没有被使用的对象,
既这些对象没有被引用。
更新中。。。。
https://blog.csdn.net/peterwin1987/article/details/7571808