android性能评测与优化-内存

    xiaoxiao2026-01-16  9

    文导读|   点击标题阅读

    互联网寒冬下,程序员如何突围提升自己?

    41岁程序员被裁,北京有1500万房产,程序员:我该不该退休?

    裸辞两个月,海投一个月,从 Android 转战 Web 前端的求职之路

    原文来源:https://www.jianshu.com/p/11eb07273478

    书籍推荐

    市面上android性能优化的书籍不多,因为性能优化这块稍微深入一点,涉及知识的深度和广度就比较大了,而且性能优化依赖很多的平台相关的工具和分析技巧,导致通用性和实效性又不太高,所以以下书籍的内容也比较浅尝辄止

    移动App性能评测与优化

    Android移动性能实战

    Android应用性能优化

    内存性能分析及优化的意义

    Overview of memory management   内存管理介绍

    OOM

    Low Memory Killer

    GC

    综上

    关注并减少应用的内存消耗可以减少oom的概率,在内存紧张的场景下获得更好的用户体验,也可以增加应用的后台存活时间

    工具介绍

    调查 RAM 使用情况

    dumpsys procstats

    用来衡量一段时间内应用消耗内存的情况

    (最小PSS-平均PSS-最大PSS/最小USS-平均USS-最大USS)

    procstats

    LeakCanary

    检测内存泄漏的工具

    MAT

    比较常用的内存dump文件分析工具

    使用方法

    hprof-conv -z src dst //-z可以排除android框架创建的对象

    使用场景

    分析场景构建

    性能测试的一些注意点

    常见的性能测试方式

    容易出现内存问题的场景

    常见的内存问题

    内存泄漏

    内存泄漏产生的原因

    一个对象的生命周期已经结束了,但是有其他对象持有了它的实例导致无法在GC时被回收,在Android中通常是Activity在finish之后依然有对象引用它导致内存泄漏

    内存泄漏的常见场景

    内存泄漏在分析工具上的表现

    内存泄漏

    每次activity的重建都会造成内存上升且gc不会使内存使用降低

    内存泄漏的避免

    内存抖动

    内存抖动的原因

    内存抖动一般是瞬间创建了大量对象,会在短时间内触发多次GC,产生卡顿

    内存抖动的场景

    内存抖动的在分析工具上的表现

    内存抖动

    制造了一个内存抖动的场景

     public void testThrashing(boolean needLog) {        int dimension = 300;        int[][] lotsOfInts = new int[dimension][dimension];        Random randomGenerator = new Random();        for (int i = 0; i < lotsOfInts.length; i++) {            for (int j = 0; j < lotsOfInts[i].length; j++) {                lotsOfInts[i][j] = randomGenerator.nextInt();            }        }        //优化以前        for (int i = 0; i < lotsOfInts.length; i++) {            String rowAsStr = "";            int[] sorted = getSorted(lotsOfInts[i]);            for (int j = 0; j < lotsOfInts[i].length; j++) {                rowAsStr += sorted[j];                if (j < (lotsOfInts[i].length - 1)) {                    rowAsStr += ", ";                }            }            if (needLog) {                Log.i(TAG, "Row " + i + ": " + rowAsStr);            }        }    }    public void optimizeThrashing() {        int dimension = 300;        int[][] lotsOfInts = new int[dimension][dimension];        Random randomGenerator = new Random();        for (int i = 0; i < lotsOfInts.length; i++) {            for (int j = 0; j < lotsOfInts[i].length; j++) {                lotsOfInts[i][j] = randomGenerator.nextInt();            }        }        //优化以后        StringBuilder sb = new StringBuilder();        for (int i = 0; i < lotsOfInts.length; i++) {            sb.delete(0, sb.length());            int[] sorted = getSorted(lotsOfInts[i]);            for (int j = 0; j < lotsOfInts[i].length; j++) {                sb.append(sorted[j]);                if (j < (lotsOfInts[i].length - 1)) {                    sb.append(", ");                }            }            Log.e(TAG, "Row " + i + ": " + sb);        }    }        int dimension = 300;        int[][] lotsOfInts = new int[dimension][dimension];        Random randomGenerator = new Random();        for (int i = 0; i < lotsOfInts.length; i++) {            for (int j = 0; j < lotsOfInts[i].length; j++) {                lotsOfInts[i][j] = randomGenerator.nextInt();            }        }        //优化以前        for (int i = 0; i < lotsOfInts.length; i++) {            String rowAsStr = "";            int[] sorted = getSorted(lotsOfInts[i]);            for (int j = 0; j < lotsOfInts[i].length; j++) {                rowAsStr += sorted[j];                if (j < (lotsOfInts[i].length - 1)) {                    rowAsStr += ", ";                }            }            if (needLog) {                Log.i(TAG, "Row " + i + ": " + rowAsStr);            }        }    }    public void optimizeThrashing() {        int dimension = 300;        int[][] lotsOfInts = new int[dimension][dimension];        Random randomGenerator = new Random();        for (int i = 0; i < lotsOfInts.length; i++) {            for (int j = 0; j < lotsOfInts[i].length; j++) {                lotsOfInts[i][j] = randomGenerator.nextInt();            }        }        //优化以后        StringBuilder sb = new StringBuilder();        for (int i = 0; i < lotsOfInts.length; i++) {            sb.delete(0, sb.length());            int[] sorted = getSorted(lotsOfInts[i]);            for (int j = 0; j < lotsOfInts[i].length; j++) {                sb.append(sorted[j]);                if (j < (lotsOfInts[i].length - 1)) {                    sb.append(", ");                }            }            Log.e(TAG, "Row " + i + ": " + sb);        }    }

    解决方案

    图片加载的内存占用

    不同dpi文件夹对图片内存的影响

    文件夹对应dpibitmap widthheightsize倍数nodpi1920108082944001xxxhdpi640132074339230400.47xxhdpi480176099069696000.84xhdpi32026401485156816001.89mdpi16052802970627264007.56 图片加载

    使用图片的建议

    RGB565

    除了图片资源的文件夹,加载图片时使用的色彩模式也影响了Bitmap大小。ARGB8888使用了32bit,所以一个像素需要4byte;RGB565使用了16bit,一个像素只需要2byte但是因为RGB565少了alpha通道,对有透明度的图片显示有问题,而且显示效果上还是有些区别,所以并不建议修改这个属性,只是在对内存有严格要求的场景下可以作为特殊手段进行优化

    ProGuard对内存的影响

    压缩代码和资源

    内存碎片

    Overview of memory management

    内存碎片

    Davik的内存回收算法不能移动对象,所以会造成一个小对象占据整个内存页,产生内存碎片而ART虚拟机的可以在GC时对内存空间进行整理,随着5.0以上系统的占有率逐渐提升,内存碎片造成的内存消耗可以不必过于关心

    其他内存问题

    Manage your app's memory

    看完本文有收获?请转发分享给更多人


    我们的知识星球第三期开期了,已达到1100人了,能连续做三期已很不容易了,有很多老用户续期,目前续期率达到50%,说明了大家对我们的知识星球还是很认可的,欢迎大家加入尽早我们的知识星球,更多星球信息参见:

    欢迎加入Java和Android架构社群

    如何进阶成为Java的Android版和架构师?

    说两件事

    微信扫描或者点击上方二维码领取的Android \ Python的\ AI \的Java等高级进阶资源

    更多学习资料点击下面的“阅读原文 ”获取

    谢谢老板,点个好看↓

    最新回复(0)