初识Android内存优化

一、简介

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);
    }
}

参考

猜你喜欢

转载自blog.csdn.net/daokedream/article/details/130179637