Android的性能优化已经是一个老生常谈的问题,最近想系统的整理一下这方面的一些资料,为了方便以后查找,特此记录。
说道性能优化,不得不去说下内存,内存在c/c++中,是靠自己去分配和释放也就是手动管理,而Java就是靠GC。其实是GC的算法去判断是否该回收,吧啦吧啦此处省略几百字……简而言之:当对象没有任何引用的时候需要被回收~~既然是判断,那么就会产生问题造成无法被回收,通俗点说就是内存不再GC的掌控之下。复杂点:当一个对象不
在使用,本应该被回收,而由于有另外一个对象持有他的引用从而造成该对象不能被回收。这种导致本该被回收的对象无法被回收而停留在堆内存中,就是内存泄漏。
首先来灌输一波定义,内存分配的3种策略:
- 静态的:静态的存储区:内存在编译的时候就已经分配好的,这块内存在运行期间一直存在,主要是放静态数据,全局的static数据和常量。
- 栈式的:在执行方法时,方法内部的成员变量都可以在栈上进行创建,函数执行结束的这些存储单元就会被释放。(这是为什么局部变量无法在方法外使用),栈内存分配的速度很快,因为是内置在处理器上的,当然容量有限。
- 堆式的:也叫动态内存分配。有时候可以new来申请分配一个内存,主要靠GC回收。所以需要在编码的时候有良好的代码习惯。
- 区别:
- 栈式的是连续的内存区域,执行先进后出的规则,进出不会产生碎片,运行效率快,但是由操作系统决定栈式内存大小。
- 堆式的是一块不连续的内存区域,管理很麻烦,频繁的进行操作,造成大量的内存碎片,慢慢的就会导致效率地下,堆式的内存比较灵活且内存区域比较大。
概念说完,还是写点代码,举个栗子
public class pepys{
int a = 1;
People p1= new People ();
public void test(){
int b = 1;
People p2 = new People ();
}
}
//这个方法里 a和p1的内存是在堆里面的,因为他们是类的成员变量。
//而方法体里面的b和p2的内存是放在栈里面的,因为他们是方法的局部变量,生命周期随着方法。
- 话题说回内存泄漏,我们所讨论的内存泄漏,主要就是堆内存,它存放的就是引用指向的对象实体。
- 在日常的开发中,总会有需求:当需要的时候可以访问到,不需要的时候可以被回收,也可以被暂时缓存以备重复利用。
- 例如:ListView、RecycleView加载大量数据或者图片的时候。
- 图片占用内存很厉害,需要管理好内存,不然很容易造成内存溢出。
- 滑出去的图片需要回收,节省内存。看ListView源码,回收对象,重用ConvertView.
- 如果用户反复滑动,或者下面还有同样的图片,就会造成多次重复IO(耗时)
- 这个时候就需要缓存,平衡好内存和IO,算法和一些特殊的Java类。
- 算法:Lrucache(最近最少回收的先回收),
- 特殊的Java类:StrongReference、SoftReference、WeakReference、PhatomRefreence.
- StringReference 强引用:
回收机制:从不回收 用途:对象的保存 生命周期:JVM终止时才会终止 - SoftReference 软引用:
回收机制:当内存不足时 使用:SoftReference 配合ReferenceQueue构造有效期 生命周期:内存不足时终止 - WeakReference 弱引用:
回收机制:GC时 使用:和软引用一致。 - PhatomReference 虚引用:
和软应用一样。 - 他们的区别并不大。其实就是会被回收时难易的级别。
- StringReference 强引用:
- 例如:ListView、RecycleView加载大量数据或者图片的时候。
开发时,为了防止内存溢出,处理一些比较占用内存大并且生命周期长的对象的时候,可以尽量使用软引用和弱引用。虽然Google现在不太推荐使用软弱引用,因为它们回收量比较大,不可控回收哪些对象,也就是任性。所以比较推荐Lrucache。
简单的说一下如何发现内存泄漏
- 主动的:平时有良好的代码规范,跳过这些坑
- 被动的:出现卡顿的时候,通过工具观察程序内存是否异常。
如果不清楚代码在何处内存泄漏.
- 凭肉眼(经验),一般来说是在程序进行某一个操作的时候卡顿。
- 借助工具:Eclipse Memory Analyzer,有独立的分析工具。