最近难得清闲,今天我就来分析下内存优化。
结构
Method Area
- 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据.
- 是线程共享区。
- 该区大小不固定、区域可以不连续,允许程序指定初始大小以及最小和最大尺寸等
- 当该区无法满足内存分配需求时,将抛出OutOfMemoryError异常
Heap
- 唯一目的:存放对象实例
- 是线程共享区。
- 垃圾管理器处理的主要区域,俗称GC堆
- 可以处于物理上不连续的内存空间中,只要逻辑上是连续的
- 当该区没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常
Stack
- 又称虚拟机栈,存放局部变量表
- 是线程私有区
- 可不连续内存空间,实际数据结构由虚拟机的实现者决定。某些实现允许用户指定栈的初始大小和最大最小值
4.若 线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。若虚拟机栈可以动态扩展,扩展时无法申请到足够的内存时会抛出OutOfMemoryError异常
Program count register
- 程序计数器,当前线程所执行的字节码的行号指示器,即标记线程的执行点,用于切换线程时恢复到准确的执行位置。
- 是线程私有区
- 无异常
Native Method Stack
- 一个Native Method就是一个java调用非java代码的接口,与虚拟机栈类似,一个为虚拟机java方法服务,一个为虚拟机native方法(非java)服务
- 是线程私有区
- 本地方法栈占用的内存区也不必是固定大小的,可以根据需要动态扩展或者收缩。某些实现也允许用户指定该内存区的初始大小以及最大,最小值。
- 若 线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。若虚拟机栈可以动态扩展,扩展时无法申请到足够的内存时会抛出OutOfMemoryError异常
优化
Method Area
任何一个Java类,包括内部类、匿名类,都要占用大概500字节的内存空间。
解决:(1)去掉没有用过的类,减少不必要类的使用
(2)慎重使用第三方jar包
(3)类的复用
(4)避免使用抽象类在使用HashMap时,即使你只设置了一个基本数据类型的键,比如说int,但是也会按照对象的大小来分配内存,大概是32字节,而不是4字节
解决:
(1)上边的问题就是自动装箱问题,办法就是使用优化过的数据集合:比如使用SparseBoolMap,SparseIntMap,SparseLongMap,LongSparseMap等使用枚举通常会比使用静态常量要消耗两倍以上的内存
解决:
使用常量
Heap
图片
解决:
(1)最近比较火的fresco图片加载框架解决图片造成的内存溢出,在安卓5.0以下是通过把图片存储到匿名共享区,然后通过引用计数的方式清除该区域的图片,就是图片的引用计数为0时,清除该图片。
(2)从上边可以看出,图片的及时释放还是很重要,后台界面释放资源可以在Activity中重写onTrimMemory()方法,然后在这个方法中监听TRIM_MEMORY_UI_HIDDEN这个级别,一旦触发了之后就说明用户已经离开了我们的程序,那么此时就可以进行资源释放操作了。
(3)加载大图学会压缩图片,切记在不占用内存的情况下获取图片大小。
(4)如果条件可以的话,尽量根据手机分辨率去获取不同大小的图片。即取己所需,不要浪费。任何一个类的实例要消耗12-16字节的内存开支,因此频繁创建实例也是会一定程序上影响内存的。
解决:
(1)尽量不要在循环中new对象,放在循环之外
(2)综合考虑,使用全局变量还是局部变量
(3)某些固定值要使用常量
(4)不要使用依赖注入框架,看上去省了不少代码,实则浪费了不少不需要的内存空间
Stack
- 布局view层次不要太深,尽量多使用RelativeLayout.
- 可以使用自定义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));
}
}
- 减少局部变量的定义
Program count register
- 暂无需优化
Native Method Stack
- 减少本地方法中的无用的变量以及方法
- 如果算法效率相当的情况下,尽量少使用本地方法,因为每次调用本地方法,都需要一个Java方法。其实是double了。