( PS:这里是在不考虑内存泄漏的前提下来讨论的。)
jvm浅析
java的jvm的内存可分为3个区:
栈区(stack):
- 每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中。
- 每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
- 栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
方法区(method):
- 又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
- 方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
堆区(heap):
- 存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)。
- jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身。
Android内核是基于jvm,所以,内存的优化,主要也是基于这三个模块来的:
栈内存优化
一般来说,安卓中的栈占用的内存非常少,在Activity/Fragment中只有一些基本数据类型的对象,和自定义对象的引用。而当Activity/Fragment被onDestroy()后,这些引用也会跟着jvm的gc流程,被销毁掉。所以,栈内存一般是不需要我们去优化的~
方法区优化
在安卓中,由于class对象和static变量是跟Application的生命周期保持一致,所以,适量控制class对象,特别是static变量,还是有必要的。
堆内存优化
这个是重中之重了,因为bitmap资源,以及各个对象本身,都存放在堆里。下面我总结几点平时的一些好的习惯:
List/Map对象及时清空
对于list这种占用内存稍大的对象,在Activity/Fragment被销毁时,也就是在它们的onDestroy()方法中,执行list.clear()。当用户在一个含有list对象的页面来回切换时,比如关闭掉此activity,然后进来,反复操作,内存会在短时间内骤增(因为系统gc也是需要一定时间的)。此时调用list.clear(),能在很大程度上避免list对象没被jvm及时gc掉,导致
内存骤增的问题。
图片bitmap对象置空
说到安卓中内存,最应该关注的就是图片问题了。对于imageView,可以在Activity/Fragment的onDestroy()调用此方法:
private void releaseImageView(ImageView imageView) {
if (null == imageView) {
return;
}
Drawable drawable = imageView.getDrawable();
if (null == drawable) {
return;
}
if (drawable instanceof BitmapDrawable) {
// 经测试,如果反复进出某个activity,而在此activity的onDestroy()中执行bitmap.recycle(),会因为回收问题而crash(),这里写出来只是想提醒大家。
// Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
// bitmap.recycle();
// bitmap = null;
}
imageView.setImageBitmap(null);//这个才是重点!
// drawable.setCallback(null); 经测试,这句也没啥用~
}
本地图片资源放置规则
安卓读取图片资源规则:
- 手机在加载图片时,会先查找自己本密度的文夹下是否存在资源,不存在则会向上查找,再向下查找,并对图片进行相应倍数的缩放。
- 如果在与自己屏幕密度相同的文件夹下存在此资源,会原样显示出来,占用内存正好是: 图片的分辨率*色彩格式占用字节数;
- 若自己屏幕密度相同的文件夹下不存在此文件,而在大于自己屏幕密度的文件夹下存在此资源,会进行缩小相应的倍数的平方;
- 若在大于自己屏幕密度的文件夹下没找到此资源,则会向小于自己屏幕密度的文件夹下查找,如果存在,则会进行放大相应的倍数的平方;
- 这两种情况图片占用内存为:
占用内存 = 图片宽度 X 图片高度/((资源文件夹密度/手机屏幕密度)^2) * 色彩格式每一个像素占用字节数。
其中:
资源文件夹密度:
dpi: 1.0
hdpi: 1.5
xhdpi: 2.0
xxhdpi: 3.0
xxxhdpi: 4.0
手机屏幕密度: 手机屏幕每英寸对应的像素点:
屏幕宽高 密度
960*540 1.5
1920*1080 3.0
2560*1440 4.0
色彩格式每一个像素占用字节数(图片默认格式为ARGB_8888):
ALPHA_8: 每个像素占用1byte内存
ARGB_4444:每个像素占用2byte内存
ARGB_8888:每个像素占用4byte内存
RGB_565: 每个像素占用2byte内存
本人测试机oppo r9s,密度为3.0(手机密度测试方法自行百度吧),测试的数据如下:
//Log.d("imageView---->", ((BitmapDrawable) iv.getDrawable()).getBitmap().getAllocationByteCount() + "");
//drawable 35059680 2510*388=973880; xhdpi 8764920; xxhdpi 3895520; xxxhdpi 2191812
研究发现:
1. 如果在与自己屏幕密度相同的文件夹下不存在此资源时,会先往上,再往下,直至找到资源,同时此资源会被缩放,资源缩放,就会有计算,消耗更多CPU。
2. 同样一张图片,放到越高分辨率的图片文件夹里,占用的内存越少。
3. 资源文件夹密度的平方,跟图片占用内存成反比。
结论:
1. 由于目前主流手机的密度都是3.0,所以,建议设计师/美工按照1080*1920进行切图,将本地图片资源放到xxhdpi文件夹下。
2. 每个图片资源文件夹下都应该配有含有全面屏的图片资源,以适配全面屏。
参考的文章
JVM 内存初学 (堆(heap)、栈(stack)和方法区(method):
android 图片占用内存大小及加载解析