一、简介
Android 内存优化是指优化 Android 应用程序的内存使用,以减少可用内存的消耗,提高应用程序的性能和可靠性。Android 内存优化可以通过减少内存使用量,减少对资源的消耗,以及提高内存利用率来实现。
安卓系统对每个应用程序都有一定的内存限制,当应用程序的内存超过了上限,就会出现 OOM (Out of Memory),也就是 App 的异常退出。因此,要改善系统的运行效率、改善用户体验、降低系统资源占用、延长电池寿命、降低系统故障的危险。Android 通过内存优化,可以减少系统内存使用,让系统更加流畅,运行更快,减少系统 Crash,提升用户体验。
二、内存优化
对于 Android 内存优化可以细分为 RAM 和 ROM 两个维度
RAM 优化
主要是降低运行时内存,RAM 优化目的有以下三个:
防止应用发生 OOM。
降低应用由于内存过大被 LMK 机制杀死的概率。
避免不合理使用内存导致 GC 次数增多,从而导致应用发生卡顿。
ROM 优化
减少程序占用的 ROM,并进行 APK 精简。其目标是减少应用程序的占用,防止由于 ROM 空间限制而导致程序的安装失败。
做好内存优化将带来以下三点好处:
第一点好处是减少 OOM,提高应用稳定性。
第二点好处是减少卡顿,提高应用流畅度。
第三点好处是减少内存占用,提高应用后台运行时的存活率。
三、内存痛点
内存痛点问题通常来说,可以细分为如下三类:
第一,内存抖动。
第二,内存泄漏。
第三,内存溢出。
内存抖动
内存波动图形呈锯齿状、GC 导致卡顿。内存抖动在 Dalvik 虚拟机上更明显,因为 ART 虚拟机内存管理、回收策略做了优化,所以内存分配、GC 效率提升了 5~10 倍,内存抖动发生概率小。
当内存频繁分配和回收导致内存不稳定,出现内存抖动,内存抖动通常表现为频繁 GC、内存曲线呈锯齿状。并且,内存抖动的危害严重,会导致页面卡顿,甚至 OOM。
这里假设有这样一个场景:点击按钮使用 Handler 发送空消息,Handler 的 handleMessage 方法接收到消息后会导致内存抖动。
for 循环创建 100 个容量为 10w+的 string[]数组在 30ms 后继续发送空消息。使用 MemoryProfiler 结合代码可找到内存抖动出现的地方。查看循环或频繁调用的地方即可。
public class MainActivity extends AppCompatActivity {
private Button mButton;
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = (Button) findViewById(R.id.button);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mHandler.sendEmptyMessage(0);
}
});
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
for (int i = 0; i < 100; i++) {
String[] arr = new String[100000];
}
mHandler.sendEmptyMessageDelayed(0, 30);
}
};
}
}
请注意,这个代码中的消息循环可能会导致内存泄漏,因此您需要在适当的时候删除消息。
内存泄漏
Android 系统虚拟机的垃圾回收是通过虚拟机 GC 机制来实现的。GC 会选择一些还存活的对象作为内存遍历的根节点 GC Roots,通过对 GC Roots 的可达性来判断是否需要回收。
内存泄漏是在当前应用周期内不再使用的对象被 GC Roots 引用,导致不能回收,使实际可使用内存变小。
对象被持有导致无法释放或不能按照对象正常的生命周期进行释放,内存泄漏导致可用内存减少和频繁 GC,从而导致内存溢出,App 卡顿。
public class MainActivity extends AppCompatActivity {
private List<Bitmap> bitmaps = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//不断加载图片并加入到List中
while (true) {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.large_image);
bitmaps.add(bitmap);
}
}
}
在上面的代码中,每次加载图片并加入到List中都不会释放内存,因为List引用了这些图片,导致图片无法释放,最终造成内存溢出。为了避免内存溢出,你可以考虑使用低内存占用的图片格式,或者在不需要使用图片时主动调用recycle方法释放图片的内存。
内存溢出(OOM)
OOM 时会导致程序异常。Android 设备出厂以后,java 虚拟机对单个应用的最大内存分配就确定下来了,超出值就会 OOM。
单个应用可用的最大内存对应于 /system/build.prop 文件中的 dalvik.vm.heap growth limit。
此外,除了因内存泄漏累积到一定程度导致 OOM 的情况以外,也有一次性申请很多内存,比如说一次创建大的数组或者是载入大的文件如图片的时候会导致 OOM。而且,实际情况下很多 OOM 就是因图片处理不当而产生的。
public class MainActivity extends AppCompatActivity {
private ImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = findViewById(R.id.image_view);
// 试图创建大的数组
int[] largeArray = new int[Integer.MAX_VALUE];
// 或者试图载入大的图片
Bitmap largeBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.large_image);
imageView.setImageBitmap(largeBitmap);
}
}
参考
-
内存指的是什么?一文带你了解Android的内存优化
-
Android性能优化:手把手带你全面了解 内存泄露 & 解决方案
-
Android 虚拟机、对象、变量的内存分配
-
Android性能调优篇之探索JVM内存分配
-
Android 从内存模型深究内存优化本质
-
深入理解Java虚拟机——Java内存区域
-
内存优化 · 基础论 · 初识Android内存优化
-
Android内存优化深入解析
-
android studio profiler 内存分析用法【官网】