android 内存优化分析

本文详细介绍了Java虚拟机中各个内存区域的特点及其可能导致的异常,并提出了针对Method Area、Heap、Stack等区域的具体优化策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近难得清闲,今天我就来分析下内存优化。

结构

这里写图片描述

Method Area

  1. 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据.
  2. 是线程共享区。
  3. 该区大小不固定、区域可以不连续,允许程序指定初始大小以及最小和最大尺寸等
  4. 当该区无法满足内存分配需求时,将抛出OutOfMemoryError异常

Heap

  1. 唯一目的:存放对象实例
  2. 是线程共享区。
  3. 垃圾管理器处理的主要区域,俗称GC堆
  4. 可以处于物理上不连续的内存空间中,只要逻辑上是连续的
  5. 当该区没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常

Stack

  1. 又称虚拟机栈,存放局部变量表
  2. 是线程私有区
  3. 可不连续内存空间,实际数据结构由虚拟机的实现者决定。某些实现允许用户指定栈的初始大小和最大最小值
    4.若 线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。若虚拟机栈可以动态扩展,扩展时无法申请到足够的内存时会抛出OutOfMemoryError异常

Program count register

  1. 程序计数器,当前线程所执行的字节码的行号指示器,即标记线程的执行点,用于切换线程时恢复到准确的执行位置。
  2. 是线程私有区
  3. 无异常

Native Method Stack

  1. 一个Native Method就是一个java调用非java代码的接口,与虚拟机栈类似,一个为虚拟机java方法服务,一个为虚拟机native方法(非java)服务
  2. 是线程私有区
  3. 本地方法栈占用的内存区也不必是固定大小的,可以根据需要动态扩展或者收缩。某些实现也允许用户指定该内存区的初始大小以及最大,最小值。
  4. 若 线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。若虚拟机栈可以动态扩展,扩展时无法申请到足够的内存时会抛出OutOfMemoryError异常

优化

Method Area

  1. 任何一个Java类,包括内部类、匿名类,都要占用大概500字节的内存空间。
    解决:

    (1)去掉没有用过的类,减少不必要类的使用
    (2)慎重使用第三方jar包
    (3)类的复用
    (4)避免使用抽象类

  2. 在使用HashMap时,即使你只设置了一个基本数据类型的键,比如说int,但是也会按照对象的大小来分配内存,大概是32字节,而不是4字节
    解决:
    (1)上边的问题就是自动装箱问题,办法就是使用优化过的数据集合:比如使用SparseBoolMap,SparseIntMap,SparseLongMap,LongSparseMap等

  3. 使用枚举通常会比使用静态常量要消耗两倍以上的内存
    解决:
    使用常量

Heap

  1. 图片
    解决:
    (1)最近比较火的fresco图片加载框架解决图片造成的内存溢出,在安卓5.0以下是通过把图片存储到匿名共享区,然后通过引用计数的方式清除该区域的图片,就是图片的引用计数为0时,清除该图片。
    (2)从上边可以看出,图片的及时释放还是很重要,后台界面释放资源可以在Activity中重写onTrimMemory()方法,然后在这个方法中监听TRIM_MEMORY_UI_HIDDEN这个级别,一旦触发了之后就说明用户已经离开了我们的程序,那么此时就可以进行资源释放操作了。
    (3)加载大图学会压缩图片,切记在不占用内存的情况下获取图片大小。
    (4)如果条件可以的话,尽量根据手机分辨率去获取不同大小的图片。即取己所需,不要浪费。

  2. 任何一个类的实例要消耗12-16字节的内存开支,因此频繁创建实例也是会一定程序上影响内存的。
    解决:
    (1)尽量不要在循环中new对象,放在循环之外
    (2)综合考虑,使用全局变量还是局部变量
    (3)某些固定值要使用常量
    (4)不要使用依赖注入框架,看上去省了不少代码,实则浪费了不少不需要的内存空间

Stack

  1. 布局view层次不要太深,尽量多使用RelativeLayout.
  2. 可以使用自定义view,简化布局层级,比如:
    这里写图片描述
    可以这么写:自定义view源码
    /** 
     * 带文字的图片 
     */  

    public class TextImageView extends ImageView {  

        protected Context mContext;  
        private float textSize; // 文字大小  
        private String text;  

        private Paint paint;  
        private TextPaint textPaint;  
        private TextImageView textImageView;  

        public TextImageView(Context context) {  
            super(context);  
            this.textImageView = this;  
            init(context);  
        }  

        public TextImageView(Context context, AttributeSet attrs, int defStyle) {  
            super(context, attrs, defStyle);  
            this.textImageView = this;  
            init(context);  
        }  

        public TextImageView(Context context, AttributeSet attrs) {  
            super(context, attrs);  
            this.textImageView = this;  
            init(context);  
        }  

        /** 
         * @param context 
         */  
        private void init(Context context) {  
            this.mContext = context;  
            paint = new Paint();  
            paint.setColor(Color.parseColor("#7f000000")); // 颜色  
            paint.setStyle(Paint.Style.FILL);  
            paint.setStrokeWidth(5);  

            textPaint = new TextPaint();// 初始化画笔,为后面文字使用。  
            textPaint.setAntiAlias(true);// 去除锯齿。  
            try {  
                textPaint.setTextSize(mContext.getResources().getDimension(  
                        R.dimen.size_28px)); // 设置文字大小  

            } catch (Exception e) {  
            }  
            textPaint.setColor(Color.WHITE); // 颜色  
            textPaint.setTextAlign(Paint.Align.CENTER);  
        }  

        @Override  
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
        }  

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

            // 画边框  
            Rect rec = canvas.getClipBounds();  
            int top = rec.bottom;  
            int left = rec.left;  
            int right = rec.right;  
            int bottom = rec.bottom;  

            RectF rectf = new RectF(left, 0.8f * top, right, bottom);  
            canvas.drawRect(rectf, paint);  
            int textWidth = 0;  
            try {  
                textWidth = (int) textPaint.measureText(text);// 获取字体的宽度  
            } catch (Exception e) {  
            }  
            String ellipsizeStr = "";  
            int boottomtextWidth = getMeasuredWidth() - 15;  
            if (textWidth > boottomtextWidth) {  
                ellipsizeStr = (String) TextUtils.ellipsize(text, textPaint,  
                        boottomtextWidth, TextUtils.TruncateAt.END);  
            } else {  
                ellipsizeStr = text;  
            }  
            if (StringTools.isNotEmpty(ellipsizeStr)) {  
                canvas.drawText(ellipsizeStr, 0.5f * right, 0.95f * top, textPaint);  
            }  

        }  

        public float getTextSize() {  
            return textSize;  
        }  

        public void setTextSize(float textSize) {  
            this.textSize = textSize;  
        }  

        public String getText() {  
            return text;  
        }  

        public void setTitle(String text) {  
            this.text = text;  
        }  

        public void setImage(String picUrl) {  
            AsynImageLoader.getInsatnce(mContext).showImageAsyn(this.textImageView,Format.null2Blank(picUrl));  
        }  

    }  
  1. 减少局部变量的定义

Program count register

  1. 暂无需优化

Native Method Stack

  1. 减少本地方法中的无用的变量以及方法
  2. 如果算法效率相当的情况下,尽量少使用本地方法,因为每次调用本地方法,都需要一个Java方法。其实是double了。

优化这条路,没有尽头,只有更好更优。还有一句话告诉大家,优化,不是所有的绝对必须按照什么来,而是要学会去选择哪种才是适合自己的最好的路。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值