性能优化01_内存泄漏

Android性能优化汇总

一 内存泄漏

1.什么是内存泄露:

内存不在GC掌控之内了
当一个对象已经不需要再使用了,本该被回收时有另外一个正在使用的对象持有它的引用从而就导致该对象不能被回收。这种本该被回收的对象不能被回收而停留在堆内存中,就会产生内存泄漏。

  • java的GC内存回收机制:某对象不再有任何的引用的时候才会进行回收。
  • C/C++ 自己去分配内存和释放内存–手动管理malloc free
    安卓的性能优化主要集中在java层。

2.内存分配的几种策略:

1)静态的

静态的存储区:内存在程序编译的时候就已经分配好,这块的内存在程序整个运行期间都一直存在。
它主要存放静态数据、全局的static数据和一些常量。

2)栈式的

在执行函数(方法)时,函数一些内部变量的存储都可以放在栈上面创建,函数执行结束的时候这些存储单元就会自动被释放掉。
栈内存包括分配的运算速度很快,因为内置在处理器的里面的。当然容量有限。

3)堆式的

也叫做动态内存分配。有时候可以用malloc或者new来申请分配一个内存。在C/C++可能需要自己负责释放(java里面直接依赖GC机制)。
在C/C++这里是可以自己掌控内存的,需要有很高的素养来解决内存的问题。java在这一块貌似程序员没有很好的方法自己去解决垃圾内存,需要的是编程的时候就要注意自己良好的编程习惯。

4)区别

  • 堆是不连续的内存区域,堆空间比较灵活也特别大。
  • 栈式一块连续的内存区域,大小是由操作系统决定的。
  • 堆管理很麻烦,频繁地new/remove会造成大量的内存碎片,这样就会慢慢导致效率低下。
  • 对于栈的话,他先进后出,进出完全不会产生碎片,运行效率高且稳定。

5)案例分析

public class Main{
	int a = 1;
	Student s = new Student();
	public void XXX(){
		int b = 1;//栈里面
		Student s2 = new Student();
	}

}
  1. 成员变量全部存储在堆中(包括基本数据类型,引用及引用的对象实体)—因为他们属于类,类对象最终还是要被new出来的。
  2. 局部变量的基本数据类型和引用存储于栈当中,引用的对象实体存储在堆中。-----因为他们属于方法当中的变量,生命周期会随着方法一起结束。

我们所讨论内存泄露,主要讨论堆内存,他存放的就是引用指向的对象实体。
有时候确实会有一种情况:当需要的时候可以访问,当不需要的时候可以被回收也可以被暂时保存以备重复使用。

比如:ListView或者GridView、REcyclerView加载大量数据或者图片的时候,
图片非常占用内存,一定要管理好内存,不然很容易内存溢出。
滑出去的图片就回收,节省内存。看ListView的源码----回收对象,还会重用ConvertView。
如果用户反复滑动或者下面还有同样的图片,就会造成多次重复IO(很耗时),
那么需要缓存—平衡好内存大小和IO,算法和一些特殊的java类。
算法:lrucache(最近最少使用先回收)
特殊的java类:利于回收,StrongReference,SoftReference,WeakReference,PhatomReference

二 四种引用

1 StrongReference强引用

回收时机:从不回收 使用:对象的一般保存 生命周期:JVM停止的时候才会终止

2 SoftReference软引用

回收时机: 当内存不足的时候;使用:SoftReference结合ReferenceQueue构造有效期短;
生命周期:内存不足时终止

3 WeakReference弱引用

回收时机: 在垃圾回收的时候;
使用: 同软引用; 生命周期:GC后终止

4 PhatomReference 虚引用

回收时机: 在垃圾回收的时候;
使用: 结合ReferenceQueue跟踪对象被垃圾回收期回收的活动;
生命周期: GC后终止

开发时,为了防止内存溢出,处理一些比较占用内存大并且生命周期长的对象的时候,可以尽量使用软引用和弱引用。
软引用比LRU算法更加任性,回收量是比较大的,你无法控制回收哪些对象。

比如使用场景:默认头像、默认图标。
ListView或者GridView、REcyclerView要使用内存缓存+外部缓存(SD卡)

三 内存泄露案例

单例模式导致内存对象无法释放而导致内存泄露

1 示例代码:

public class CommonUtil {

    private static CommonUtil instance;
    private Context context;

    private CommonUtil(Context context) {
        this.context = context;
    }

    public static CommonUtil getInstance(Context context){
        if(instance == null){
            instance = new CommonUtil(context);
        }
        return instance;
    }
}

使用:

public class LeakageActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        CommonUtil commonUtil = CommonUtil.getInstance(this);

    }
}

2 出现现象

横屏后内存使用加大
在这里插入图片描述

3 原因分析:

由于使用了单例,横屏后CommonUtil仍使用之前的Activity的实例,其未被回收,可以使用dump java Heap 查看:
在这里插入图片描述

4 结论:

旋转3次:会在内存里面开辟三个Activity,实际上3次以上都只会有2个Activity。当GC回收的时候会将除了第0个和最后这一个留着其他的都会被回收;导致MainActivity在内存当中泄露了,CommonUtil生命周期跟MainActivity不一致,而是跟Application进程同生同死。
note: 能用Application的context就用Application的
解决方法: getInstance(this)的this替换成AplicationContext

四 Demo

LeakageActivity

发布了211 篇原创文章 · 获赞 63 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/baopengjian/article/details/103880467