内存优化:内存抖动和内存泄漏

Posted 斯音

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内存优化:内存抖动和内存泄漏相关的知识,希望对你有一定的参考价值。

LMK;内存抖动、内存泄漏与内存溢出
优化的结果:使得app流畅不卡

内存抖动

短时间内有大量对象创建与销毁,它伴随着频繁的GC。

比较典型的就是字符串的拼接造成内存抖动。

比如:

String str = "";
for(int i=0; i< 10; i++) 
    str += i;

+=操作会编译成StringBuilder对象,然后调用StringBuilder的append方法进行拼接。
所以上述代码会创建10个StringBuilder对象,每执行一次+操作都会新创建一个StringBuilder对象。

优化方法:避免使用+或者+=操作,使用StringBuilder来实现字符串的拼接

StringBuilder str = new StringBuilder();
for (int i = 0; i < 10; i++) 
    str.append(i);

内存抖动实战

一个不断旋转的试图,优化前的代码:

public class iosStyleLoadingView1 extends View 

    private final Context context;
    private double radius;
    private double insideRadius;
    private float northwestXStart;
    private float northwestYStart;
    private float northXStart;
    private float northYStart;
    private float notheastXStart;
    private float notheastYStart;
    private float eastXStart;
    private float eastYStart;
    private float southeastXStart;
    private float southeastYStart;
    private float southXStart;
    private float southYStart;
    private float southwestXStart;
    private float southwestYStart;
    private float westXStart;
    private float westYStart;

    private float northwestXEnd;
    private float northwestYEnd;
    private float northXEnd;
    private float northYEnd;
    private float notheastXEnd;
    private float notheastYEnd;
    private float eastXEnd;
    private float eastYEnd;
    private float southeastXEnd;
    private float southeastYEnd;
    private float southXEnd;
    private float southYEnd;
    private float southwestXEnd;
    private float southwestYEnd;
    private float westXEnd;
    private float westYEnd;

    private int currentColor = 7;

    String color[] = new String[]
            "#a5a5a5",
            "#b7b7b7",
            "#c0c0c0",
            "#c9c9c9",
            "#d2d2d2",
            "#dbdbdb",
            "#e4e4e4",
            "#e4e4e4"
    ;

    public IOSStyleLoadingView1(Context context) 
        this(context,null,0);
    

    public IOSStyleLoadingView1(Context context, AttributeSet attrs) 
        this(context,null,0);
    

    public IOSStyleLoadingView1(Context context, AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
        this.context = context;
        radius = UIKits.dip2Px(context, 9);
        insideRadius = UIKits.dip2Px(context, 5);
    

    @Override
    protected void onDraw(Canvas canvas) 
        super.onDraw(canvas);

        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStrokeWidth(UIKits.dip2Px(context, 2));
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeCap(Paint.Cap.ROUND);

        Path p0 = new Path();
        paint.setColor(Color.parseColor(color[0]));//Color.parseColor方法会调用String的substring()方法,substring()方法会产生新的String对象
        p0.moveTo(northwestXStart, northwestYStart);
        p0.lineTo(northwestXEnd, northwestYEnd);
        canvas.drawPath(p0, paint);

        Path p1 = new Path();
        paint.setColor(Color.parseColor(color[1]));
        p1.moveTo(northXStart, northYStart);
        p1.lineTo(northXEnd, northYEnd);
        canvas.drawPath(p1, paint);

        Path p2 = new Path();
        paint.setColor(Color.parseColor(color[2]));
        p2.moveTo(notheastXStart, notheastYStart);
        p2.lineTo(notheastXEnd, notheastYEnd);
        canvas.drawPath(p2, paint);

        Path p3 = new Path();
        paint.setColor(Color.parseColor(color[3]));
        p3.moveTo(eastXStart, eastYStart);
        p3.lineTo(eastXEnd, eastYEnd);
        canvas.drawPath(p3, paint);

        Path p4 = new Path();
        paint.setColor(Color.parseColor(color[4]));
        p4.moveTo(southeastXStart, southeastYStart);
        p4.lineTo(southeastXEnd, southeastYEnd);
        canvas.drawPath(p4, paint);

        Path p5 = new Path();
        paint.setColor(Color.parseColor(color[5]));
        p5.moveTo(southXStart, southYStart);
        p5.lineTo(southXEnd, southYEnd);
        canvas.drawPath(p5, paint);

        Path p6 = new Path();
        paint.setColor(Color.parseColor(color[6]));
        p6.moveTo(southwestXStart, southwestYStart);
        p6.lineTo(southwestXEnd, southwestYEnd);
        canvas.drawPath(p6, paint);

        Path p7 = new Path();
        paint.setColor(Color.parseColor(color[7]));
        p7.moveTo(westXStart, westYStart);
        p7.lineTo(westXEnd, westYEnd);
        canvas.drawPath(p7, paint);

    

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        double centerX = getMeasuredWidth() / 2;
        double centerY = getMeasuredHeight() / 2;
        double leg = radius * Math.cos(Math.PI / 4);
        double insideLeg = insideRadius * Math.cos(Math.PI / 4);

        northwestXStart = (float) (centerX - leg);
        northwestYStart = (float) (centerY - leg);
        northXStart = (float) centerX;
        northYStart = (float) (centerY - radius);
        notheastXStart = (float) (centerX + leg);
        notheastYStart = (float) (centerY - leg);
        eastXStart = (float) (centerX + radius);
        eastYStart = (float) centerY;
        southeastXStart = (float) (centerX + leg);
        southeastYStart = (float) (centerY + leg);
        southXStart = (float) centerX;
        southYStart = (float) (centerY + radius);
        southwestXStart = (float) (centerX - leg);
        southwestYStart = (float) (centerY + leg);
        westXStart = (float) (centerX - radius);
        westYStart = (float) centerY;

        northwestXEnd = (float) (centerX - insideLeg);
        northwestYEnd = (float) (centerY - insideLeg);
        northXEnd = (float) centerX;
        northYEnd = (float) (centerY - insideRadius);
        notheastXEnd = (float) (centerX + insideLeg);
        notheastYEnd = (float) (centerY - insideLeg);
        eastXEnd = (float) (centerX + insideRadius);
        eastYEnd = (float) centerY;
        southeastXEnd = (float) (centerX + insideLeg);
        southeastYEnd = (float) (centerY + insideLeg);
        southXEnd = (float) centerX;
        southYEnd = (float) (centerY + insideRadius);
        southwestXEnd = (float) (centerX - insideLeg);
        southwestYEnd = (float) (centerY + insideLeg);
        westXEnd = (float) (centerX - insideRadius);
        westYEnd = (float) centerY;
    

    @Override
    protected void onAttachedToWindow() 
        super.onAttachedToWindow();
        startAnimation();
    

    private ValueAnimator valueAnimator;

    public void startAnimation() 
        valueAnimator = ValueAnimator.ofInt(7, 0);
        valueAnimator.setDuration(400);
        valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() 
            @Override
            public void onAnimationUpdate(ValueAnimator animation) 
                if ((int) animation.getAnimatedValue() != currentColor) 
                    String b[] = new String[color.length];//移动后的数组
                    for (int c = 0, size = color.length - 1; c < size; c++) 
                        b[c + 1] = color[c];
                    
                    b[0] = color[color.length - 1];
                    color = b;
                    invalidate();
                    currentColor = (int) animation.getAnimatedValue();
                
            
        );
        valueAnimator.start();
    


利用android Studio内置的Profiler工具查看内存使用情况:

可以看到内存产生抖动,并且随着程序的运行,内存一直在增加,选择一段时间分析内存中的对象:

可以看到内存中的有大量的Path对象和String对象,一般来说是有异常的,分析代码看看是否能避免这种情况。

可以在onDraw方法里进行优化,优化后的代码:

/**
 * 优化后的IOSStyleLoadingView
 * onDraw方法里不创建Path和Paint对象,不调用Color.parseColor创建String对象
 *
 */
public class IOSStyleLoadingView extends View 

    private final Context context;
    private double radius;
    private double insideRadius;
    private float northwestXStart;
    private float northwestYStart;
    private float northXStart;
    private float northYStart;
    private float notheastXStart;
    private float notheastYStart;
    private float eastXStart;
    private float eastYStart;
    private float southeastXStart;
    private float southeastYStart;
    private float southXStart;
    private float southYStart;
    private float southwestXStart;
    private float southwestYStart;
    private float westXStart;
    private float westYStart;

    private float northwestXEnd;
    private float northwestYEnd;
    private float northXEnd;
    private float northYEnd;
    private float notheastXEnd;
    private float notheastYEnd;
    private float eastXEnd;
    private float eastYEnd;
    private float southeastXEnd;
    private float southeastYEnd;
    private float southXEnd;
    private float southYEnd;
    private float southwestXEnd;
    private float southwestYEnd;
    private float westXEnd;
    private float westYEnd;

    private int currentColor = 7;

    String color[] = new String[]
            "#a5a5a5",
            "#b7b7b7",
            "#c0c0c0",
            "#c9c9c9",
            "#d2d2d2",
            "#dbdbdb",
            "#e4e4e4",
            "#e4e4e4"
    ;

    int[] colors = new int[8];

    public IOSStyleLoadingView(Context context) 
        this(context, null, 0);
    

    public IOSStyleLoadingView(Context context, AttributeSet attrs) 
        this(context, null, 0);
    

    /**
     * 提前在构造方法里调用Color.parseColor解析好数据,而不是在onDraw方法里频繁调用
     *
     *
     * @param context
     * @param attrs
     * @param defStyleAttr
     */
    public IOSStyleLoadingView(Context context, AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
        this.context = context;
        radius = UIKits.dip2Px(context, 9);
        insideRadius = UIKits.dip2Px(context, 5);

        for (int i = 0; i < color.length; i++) 
            colors[i] = Color.parseColor(color[i]);
        

        paint.setAntiAlias(true);
        paint.setStrokeWidth(UIKits.dip2Px(context, 2));
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeCap(Paint.Cap.ROUND);
    

    Path path = new Path();
    Paint paint = new Paint();

    @Override
    protected void onDraw(Canvas canvas) 
        super.onDraw(canvas);

        path.reset();
        paint.setColor(colors[(currentColor++) % 8]);
        path.moveTo(northwestXStart, northwestYStart);
        path.lineTo(northwestXEnd, northwestYEnd);
        canvas.drawPath(path, paint);

        path.reset();
        paint.setColor(colors[(currentColor++) % 8]);
        path.moveTo(northXStart, northYStart);
        path.lineTo(northXEnd, northYEnd);
        canvas.drawPath(path, paint);

        path.reset();
        paint.setColor(colors[(currentColor++) % 8]);
        path.moveToAndroid 性能优化--内存优化--(内存优化工具内存管理机制内存抖动内存泄漏)

Android 性能优化--内存优化--(内存优化工具内存管理机制内存抖动内存泄漏)

Android 性能优化--内存优化--(内存优化工具内存管理机制内存抖动内存泄漏)

Android App卡顿慢优化之解决内存抖动及内存泄漏

内存优化:Profile内存检测工具

Android技术分享| Android 中部分内存泄漏示例及解决方案