1.前言
-
内存调优
是体现Android性能主要部分之一。 -
内存调优
中包含三种比较常见的场景,分别是内存泄漏、内存溢出、内存抖动。- 内存泄漏:指某块内存在使用完毕之后,还被其他对象引用着,导致GC回收不了。(生命周期长的对象引用生命周期短的对象)
- 内存溢出:指应用内存超出Android系统赋予给每一个应用最大容量的限制。(OutOfMemory)
- 内存溢出:指在短时间内有大量的对象被创建或者被回收的现象。(循环种大量创建对象、回收内存GC)
- 今天主要分享前2种场景。
- 在分享前,先了解或者复习以下的技术点,以便更好理解后续内容:
1.1 内存
JAVA是在JVM所虚拟出的内存环境中运行的,主要的内存区:堆、栈和方法区。
栈(stack):是简单的数据结构与变量,程序运行时系统自动分配,使用完毕后自动释放。优点:速度快。
堆(heap):用于存放由new创建的对象和数组。在堆中分配的内存,一方面由java虚拟机自动垃圾回收器来管理,另一方面还需要程序员提供修养,防止内存泄露问题。
方法区(method):又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
1.2 Java GC
GC可以自动清理堆中不在使用(不在有对象持有该对象的引用)的对象。
在JAVA中对象如果再没有引用指向该对象,那么该对象就无从处理或调用该对象,这样的对象称为不可到达(unreachable)。垃圾回收用于释放不可到达的对象所占据的内存。
对android来说,内存使用尤为吃紧,最开始的app进程最大分配才8M的内存,渐渐增加到16M、32M、64M,但是和服务端相比还是很渺小的。如果对象回收不及时,很容易出现OOM错误。
- 文章中实例 linhaojian的Github
2.内存泄漏
2.1 场景
- 罗列一些会导致
内存泄漏
的场景,如下:- 单例中引用Activity对象;
- 静态(static)变量中持有生命周期短的对象;
- 匿名内部类中执行耗时任务(Handler);
- 非静态内部类(MyThread);
- 异步处理耗时任务,在finish时未终止(new Thread);
- 资源对象未关闭或者释放(Cursor、Bitmap);
2.2 处理
-
内存泄漏
的 2 种检测方式:Android Profile & LeakCanary。 - 通过异步处理耗时任务的例子,讲解 2 种检测方式,代码如下:
public class LeakCanaryActivity extends AppCompatActivity {
private Button btn_leakcanary;
private Leak leak;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.leakcanary);
leak = new Leak();
btn_leakcanary = findViewById(R.id.btn_leakcanary);
btn_leakcanary.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//开启内存泄漏
leak.staticRun();
}
});
}
class Leak {
public Leak(){
}
public void staticRun(){
new Thread(){
@Override
public void run() {
while(true){
Log.i("linhaojian","内存泄漏中....."+Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
}
2.2.1 Android Profile
- Android Profile 是Android Studio自带的检测性能相关的功能,通过Android Profile如何分析上述例子泄露的问题;
-
1.第一次运行上述例子 & finish上述Activity,然后点击回收功能,看看Android Profile的Memory变化:
-
2.继续执行第一步的操作,结果如下:
从上述图一,可以发现LeakCanaryActivity类 finish & GC 之后,都是还是存在于内存中,说明已经造成了内存泄漏。
从上述图二,可以发现重复执行第一步的操作之后,在Allocation中,LeakCanaryActivity对象在内存中造成了 2 个对象未被回收。
2.2.2 LeakCanary
- LeakCanary 是由Square公司维护的一个内存泄漏检测框架库。
- 引入
dependencies {
//...
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.2'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.2'
// Optional, if you use support library fragments:
debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.2'
}
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
//初始化
if(!LeakCanary.isInAnalyzerProcess(this)){
LeakCanary.install(this);
}
}
}
- 使用
- 运行上述的例子,看看手机上的通知栏变化:
- 点击图上的ReportFragment leaked,查看泄漏的相关信息,如下图:
- 展开红色框选型,就能详细查看具体内存泄漏的位置:
内存泄漏发生的类:LeakCanaryActivity中内部类Leak;
内存泄漏发生的线程:staticRun函数中的Thread(name:Thread-2);
3.内存溢出
3.1 场景
- 罗列一些会导致
内存溢出
的场景,如下:- 大量使用Bitmap;
- 内存中存在大量的强引用对象;
3.2 处理
1.适合选择不同的引用方式创建对象(强、软、弱、虚);
2.在必要时块合理的按模划分**多进程(android:process=":xxx")**开发,分散内存占用;
3.内存对象的重复利用;
4.优化布局层次,重用,需要时加载ViewStub,减少内存消耗;
4.总结
- 到此,
内存调优
介绍完毕。 - 如果喜欢我的分享,可以点击 关注 或者 赞,你们支持是我分享的最大动力 。
- linhaojian的Github
欢迎关注linhaojian_CSDN博客或者linhaojian_简书!
不定期分享关于安卓开发的干货。
写技术文章初心
- 技术知识积累
- 技术知识巩固
- 技术知识分享
- 技术知识交流