android bitmap内存那些事
2016-01-10 14:05
816 查看
bitmap内存占用是一个困扰我很久的问题,今天就好好理一理!
这篇文章用的图片为下面这张图:480 * 800 (这里放上来的我把它缩小了,要不然网页上显示着特别大)
问题:一张压缩前 和 压缩后的图片,PC上的大小有区别,但尺寸不变,只是质量压缩了,那么读入内存时占用的内存大小呢?
验证:
机型:小米2s
压缩前的图大小如下:
压缩后的图大小如下:
加载10张该图,三次打印内存分别对应:加载前、加载后、释放后
下面的内存解释:
maxMem(Runtime.getRuntime().maxMemory()):可以分配给一个应用程序的最大内存;
totalMem(Runtime.getRuntime().totalMemory()) : 当前实际分配给该应用程序的内存;
freeMem(Runtime.getRuntime().freeMemory()):当前实际分配的内存中空闲的内存
totalMem - freeMem 就是当前应用程序实际消耗的内存
代码:
加载压缩前的10张图片,内存占用如下:
把图片在线压缩,从原来的267kb压缩到85kb,再加载到内存, 占用的内存仍然是一样多
从上述数据看出,无论图片质量好坏,加载到内存中占用内存的大小只与图片大小、Config图片质量参数配置有关,压缩图片只是让打包的apk减小,而运行时的内存大小是无关的。
ps: 上图中看出小米2S给一个应用程序分配的最大内存时96M,
android的config有以下几种:
ALPHA_8——代表8位Alpha位图
ARGB_4444——代表16位ARGB位图
ARGB_8888——代表32位ARGB位图
RGB_565——代表8位RGB位图
理论:图片占用的内存大小,根据bitmap的Config设置,不同质量占不同的大小,如 ARGB_8888,一个像素占4字节,一张 480*800 ,看做横竖每交叉点一个像素
总内存 = 4 * 像素点数量 = 4 * (480 * 800)= 1536000byte = 1500KB=1.5M
但上图中计算出一张图片占用的内存并非1.5M,而是 ((42 - 5)- (18 - 8)) / 10 = 2.7M (这是初略计算),这是为什么? 难道理论有错??
当然理论没错了,原因是我把图片放在了 drawable-hdpi目录下,小米2S机型对应的是 drawable-xhdpi目录,android系统会自动把 hdpi目录下的图片等比例放大约1.8倍导致的,放大的倍数根机型有关,如果不希望被放大,那么就把图片放在xhdpi目录下;这就涉及到机型适配问题了
问题:图片加载出现out of memory,亲眼见识
测试机型:HUAWEI G750-T00
加载40张 480 * 800的图片,内存占用如下图, 实测加载到46张图片时,outOverMemory,
ps: android一个应用程序可分配的最大内存默认16M,不同手机版本和型号有所不同,
参考链接: http://zhidao.baidu.com/link?url=2uJUgMpXT5sgXbvgf0Nm-090nsP0PXe_77Afsmtc1-Rt6z3-5v-8oozVNQA3Rz6UBZVZzlCTXpVm2O_mMvnqMW6a1YwPL_ejbuTpWWhUMlm
PS:HUAWEI G750-T00 释放图片后,分配的内存立即恢复到加载图片前4M,但小米2S在释放后,虽然回收了,空闲内存多了,但分配给当前应用程序的内存并没恢复,还是那么多42M,每个机型的实现机制不一样。
如果每次加载到内存后,立即释放,则可以无限制加载,不会出现out of memory,看来调用recycle之后,虽然不立即释放,但当内存不足时还是会去释放的,并分配给新对象
打出来的totalMem 一直是18M,说明一直在释放,测试机型:小米2S
所以bitmap用完后recycle是很有必要的
ps: 测试发现,HUAWEI G750-T00空应用占约(4-1)=3M内存,小米2s占约(18-8)=10M内存 , 区别这么夸张?
问题:imageView 显示一个图片,内存占用是否与加载到bitmap一样多? imageview所在的activity finish后,占用的内存是否释放掉?立即释放还是内存不足时释放?
验证: 图片显示到imageView 和 解码到bitmap,占用内存一样多;
activity finish后,可能不会立即释放内存,但内存不足时应该是会释放掉的,只要这个activity里的对象没被其他类引用;所以android里不用过于关注activity里各种内存占用、释放,只要引用不被其他类引用
问题:xml里设置ImageView的src 和 代码中设置imageResource占用的内存哪个大?
验证:
机型:HUAWEI G750-T00,图片尺寸:480*800
(1)xml设置图片
内存:
(2)代码设置图片
内存:
从上图而知,占用的内存一样多。
那么如果imageView的width 不是wrap_content呢?比如 width=100,height=150
经测试,占用的内存情况与上述wrap_content完全相同; 看来android底层加载图片时,也没有进行内存优化,未根据imageView控件大小对图片做缩放后再加载进来;或者什么原因不优化
问题:bitmap加载内存优化,究竟能优化多少
验证:机型HUAWEI G750-T00,加载 480*800的10张图片
(1) 普通的 BitmapFactory.
decodeResource 占用内存:
(2) 通过
Options优化,目标大小为200*200,根据期望的大小加载图片:
上面两个结果,普通方式占用32M,优化后占用5M,效果是显著的; 对于优化大图加载效果显著,如果是很小的图就不一定要这么优化;
PS:图片优化不要在UI线程
代码:
问题:创建一个空的bitmap 480*800, 占用的内存大小是不是与 加载一张480*800的图片大小一样?
验证:机型HUAWEI G750-T00,
(1)加载本文中480*800的10张图片(放在 drawable-xhdpi目录)
(2)
Bitmap.createBitmap(480, 800, Config.ARGB_8888) 10张:
从上图看出,占用内存一样多,证明 理论是正确的,bitmap占用内存大小 与三个因素有关: width 、height、config质量参数
代码:
问题:同一张图片,在不同的机型上占用的内存是否相同
验证:机型:HUAWEI G750-T00、小米2S
加载10张 480*800的图片,三条日志对应:加载前、加载后、recycle后
华为内存:
图片占用内存= (33 - 1)- (4 - 1)= 29M
小米2S内存:
图片占用内存=( 42 -5 )- (18 - 8)= 27M
这只是大概的计算,两者差不多,这只是初略计算,用下面的代码可以更精确的读取bitmap占的内存大小
问题:android bitmap占用的内存代码直接计算
验证:
代码: int bitmapMem = bitmap.getByteCount(); (ps: 还有别的计算方式,可以在网上查)
bitmapMem = 1.5M
问题:PC上看 480*800的图片,读入内存bitmap后大小 getWidth getHeight值变了
getWidth: 640, getHeight: 1067
原因:drawable存放目录导致,我放在drawable-hdpi目录下,机型是 720*1080分辨率的(320密度),
改放在 drawable-xhdpi目录下,bitmap.getWidth() 值就与预期一致了为480,竟然还有这学问?!
drawable目录,android图片处理有自动缩放功能,如果xhdpi的机型,读取hdpi目录,则会认为图适配的机型是hdpi,用在 xhdpi的机型需要等比例放大,故出现这样的问题
PS:测试的代码工程上传到csdn了,有兴趣的可以交流交流哦,地址:http://download.csdn.net/detail/duantihi/9398161
这篇文章用的图片为下面这张图:480 * 800 (这里放上来的我把它缩小了,要不然网页上显示着特别大)
问题:一张压缩前 和 压缩后的图片,PC上的大小有区别,但尺寸不变,只是质量压缩了,那么读入内存时占用的内存大小呢?
验证:
机型:小米2s
压缩前的图大小如下:
压缩后的图大小如下:
加载10张该图,三次打印内存分别对应:加载前、加载后、释放后
下面的内存解释:
maxMem(Runtime.getRuntime().maxMemory()):可以分配给一个应用程序的最大内存;
totalMem(Runtime.getRuntime().totalMemory()) : 当前实际分配给该应用程序的内存;
freeMem(Runtime.getRuntime().freeMemory()):当前实际分配的内存中空闲的内存
totalMem - freeMem 就是当前应用程序实际消耗的内存
代码:
/** * 图片占用内存测试 */ public static void bmpMemTest() { //打印内存占用 LoggerEx.printMem("before load "); int cnt = 10; Bitmap [] bms = new Bitmap[cnt]; for(int i = 0; i < cnt; i++) { bms[i] = BitmapFactory.decodeResource(App.instance().getResources(), R.drawable.guess_game_bg); //guess_game_bg_compressed 是在PC上压缩后的图 // bms[i] = BitmapFactory.decodeResource(App.instance().getResources(), R.drawable.guess_game_bg_compressed); //及时recycle,程序内存不足时,会去释放之前占用过的内存,所以加载多少图片都不会 out of memory // bms[i].recycle(); } try { Thread.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } LoggerEx.printMem("after load "); for(int i = 0; i < cnt; i++){ if(!bms[i].isRecycled()) { bms[i].recycle(); bms = null; //设置null 这个对象才会gc的时候回收 } } System.gc(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } LoggerEx.printMem("after recycle"); }
/** * 打印当前程序占用的内存 */ public static void printMem(String when) { //程序可用的最大内存 float maxMem = Runtime.getRuntime().maxMemory() / 1024 / 1024; //程序当前占用的内存 float totalMem = Runtime.getRuntime().totalMemory() / 1024 / 1024; //freeMem != maxMem - totalMem //我理解 freeMem应该是 当前分配给该程序的内存 - totalMem, 当前分配给程序的内存时动态的(在小于maxMem范围内) //同virtualbox安装的ubuntu虚拟机占用内存类似,设置个最大内存,但实际占用内存时动态分配的 float freeMem = Runtime.getRuntime().freeMemory() / 1024 / 1024; bm(when + ": maxMem | totalMem | freeMem : " + maxMem + "M|" + totalMem + "M|" + freeMem + "M"); }
加载压缩前的10张图片,内存占用如下:
把图片在线压缩,从原来的267kb压缩到85kb,再加载到内存, 占用的内存仍然是一样多
从上述数据看出,无论图片质量好坏,加载到内存中占用内存的大小只与图片大小、Config图片质量参数配置有关,压缩图片只是让打包的apk减小,而运行时的内存大小是无关的。
ps: 上图中看出小米2S给一个应用程序分配的最大内存时96M,
android的config有以下几种:
ALPHA_8——代表8位Alpha位图
ARGB_4444——代表16位ARGB位图
ARGB_8888——代表32位ARGB位图
RGB_565——代表8位RGB位图
理论:图片占用的内存大小,根据bitmap的Config设置,不同质量占不同的大小,如 ARGB_8888,一个像素占4字节,一张 480*800 ,看做横竖每交叉点一个像素
总内存 = 4 * 像素点数量 = 4 * (480 * 800)= 1536000byte = 1500KB=1.5M
但上图中计算出一张图片占用的内存并非1.5M,而是 ((42 - 5)- (18 - 8)) / 10 = 2.7M (这是初略计算),这是为什么? 难道理论有错??
当然理论没错了,原因是我把图片放在了 drawable-hdpi目录下,小米2S机型对应的是 drawable-xhdpi目录,android系统会自动把 hdpi目录下的图片等比例放大约1.8倍导致的,放大的倍数根机型有关,如果不希望被放大,那么就把图片放在xhdpi目录下;这就涉及到机型适配问题了
问题:图片加载出现out of memory,亲眼见识
测试机型:HUAWEI G750-T00
加载40张 480 * 800的图片,内存占用如下图, 实测加载到46张图片时,outOverMemory,
ps: android一个应用程序可分配的最大内存默认16M,不同手机版本和型号有所不同,
参考链接: http://zhidao.baidu.com/link?url=2uJUgMpXT5sgXbvgf0Nm-090nsP0PXe_77Afsmtc1-Rt6z3-5v-8oozVNQA3Rz6UBZVZzlCTXpVm2O_mMvnqMW6a1YwPL_ejbuTpWWhUMlm
PS:HUAWEI G750-T00 释放图片后,分配的内存立即恢复到加载图片前4M,但小米2S在释放后,虽然回收了,空闲内存多了,但分配给当前应用程序的内存并没恢复,还是那么多42M,每个机型的实现机制不一样。
如果每次加载到内存后,立即释放,则可以无限制加载,不会出现out of memory,看来调用recycle之后,虽然不立即释放,但当内存不足时还是会去释放的,并分配给新对象
打出来的totalMem 一直是18M,说明一直在释放,测试机型:小米2S
所以bitmap用完后recycle是很有必要的
ps: 测试发现,HUAWEI G750-T00空应用占约(4-1)=3M内存,小米2s占约(18-8)=10M内存 , 区别这么夸张?
问题:imageView 显示一个图片,内存占用是否与加载到bitmap一样多? imageview所在的activity finish后,占用的内存是否释放掉?立即释放还是内存不足时释放?
验证: 图片显示到imageView 和 解码到bitmap,占用内存一样多;
activity finish后,可能不会立即释放内存,但内存不足时应该是会释放掉的,只要这个activity里的对象没被其他类引用;所以android里不用过于关注activity里各种内存占用、释放,只要引用不被其他类引用
问题:xml里设置ImageView的src 和 代码中设置imageResource占用的内存哪个大?
验证:
机型:HUAWEI G750-T00,图片尺寸:480*800
(1)xml设置图片
内存:
(2)代码设置图片
内存:
从上图而知,占用的内存一样多。
那么如果imageView的width 不是wrap_content呢?比如 width=100,height=150
经测试,占用的内存情况与上述wrap_content完全相同; 看来android底层加载图片时,也没有进行内存优化,未根据imageView控件大小对图片做缩放后再加载进来;或者什么原因不优化
问题:bitmap加载内存优化,究竟能优化多少
验证:机型HUAWEI G750-T00,加载 480*800的10张图片
(1) 普通的 BitmapFactory.
decodeResource 占用内存:
(2) 通过
Options优化,目标大小为200*200,根据期望的大小加载图片:
上面两个结果,普通方式占用32M,优化后占用5M,效果是显著的; 对于优化大图加载效果显著,如果是很小的图就不一定要这么优化;
PS:图片优化不要在UI线程
代码:
<span style="font-size:14px;"> /** * bitmap加载优化,测试优化后占用内存大小 */ public static void optimizeBmMem() { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(App.instance().getResources(), R.drawable.guess_game_bg, options); options.inSampleSize = calculateInSampleSize(options, 200, 200); options.inJustDecodeBounds = false; LoggerEx.printMem("before load"); int cnt = 10; Bitmap []bitmaps = new Bitmap[cnt]; for(int i = 0; i < cnt; i++) { bitmaps[i] = BitmapFactory.decodeResource(App.instance().getResources(), R.drawable.guess_game_bg, options); } LoggerEx.printMem("after load"); for(int i = 0; i < cnt; i++) { bitmaps[i].recycle(); } } /** * 计算图片采样率 * @param options * @param reqWidth * @param reqHeight * @return */ private static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) { inSampleSize *= 2; } long totalPixels = width / inSampleSize * height / inSampleSize ; final long totalReqPixelsCap = reqWidth * reqHeight * 2; while (totalPixels > totalReqPixelsCap) { inSampleSize *= 2; totalPixels /= 2; } } return inSampleSize; }</span>
问题:创建一个空的bitmap 480*800, 占用的内存大小是不是与 加载一张480*800的图片大小一样?
验证:机型HUAWEI G750-T00,
(1)加载本文中480*800的10张图片(放在 drawable-xhdpi目录)
(2)
Bitmap.createBitmap(480, 800, Config.ARGB_8888) 10张:
从上图看出,占用内存一样多,证明 理论是正确的,bitmap占用内存大小 与三个因素有关: width 、height、config质量参数
代码:
<span style="font-size:14px;"> public static void emptyBmp() { LoggerEx.printMem("before load"); int cnt = 1; Bitmap []bitmaps = new Bitmap[cnt]; for(int i = 0; i < cnt; i++) { bitmaps[i] = Bitmap.createBitmap(480, 800, Config.ARGB_8888); } LoggerEx.printMem("after load"); for(int i = 0; i < cnt; i++) { if(!bitmaps[i].isRecycled()) { bitmaps[i].recycle(); bitmaps[i] = null; } } }</span>
问题:同一张图片,在不同的机型上占用的内存是否相同
验证:机型:HUAWEI G750-T00、小米2S
加载10张 480*800的图片,三条日志对应:加载前、加载后、recycle后
华为内存:
图片占用内存= (33 - 1)- (4 - 1)= 29M
小米2S内存:
图片占用内存=( 42 -5 )- (18 - 8)= 27M
这只是大概的计算,两者差不多,这只是初略计算,用下面的代码可以更精确的读取bitmap占的内存大小
问题:android bitmap占用的内存代码直接计算
验证:
代码: int bitmapMem = bitmap.getByteCount(); (ps: 还有别的计算方式,可以在网上查)
bitmapMem = 1.5M
问题:PC上看 480*800的图片,读入内存bitmap后大小 getWidth getHeight值变了
getWidth: 640, getHeight: 1067
原因:drawable存放目录导致,我放在drawable-hdpi目录下,机型是 720*1080分辨率的(320密度),
改放在 drawable-xhdpi目录下,bitmap.getWidth() 值就与预期一致了为480,竟然还有这学问?!
drawable目录,android图片处理有自动缩放功能,如果xhdpi的机型,读取hdpi目录,则会认为图适配的机型是hdpi,用在 xhdpi的机型需要等比例放大,故出现这样的问题
PS:测试的代码工程上传到csdn了,有兴趣的可以交流交流哦,地址:http://download.csdn.net/detail/duantihi/9398161
相关文章推荐
- android4.4组件分析--service组件
- Android之DialogFragment
- android 一些总结 1.2-1.10
- Android卡片式列表布局
- B站的DanmakuFlameMaster的使用
- android怎样查看当前project哪些profile是打开的
- Android(Lollipop/5.0) Material Design(四) 创建列表和卡片
- 如何更换Android模拟器界面
- Android应用程序四大组件
- Android开发指南-窗口小部件(App Widgets)
- Android开发指南-三维图形
- Android开发指南-二维图形
- Android开发指南-工具-画九宫格
- android笔记(一)
- android颜色设置
- [置顶] Android性能优化笔记
- Android-ListView卡顿优化
- 深入浅出RxJava四-在Android中使用响应式编程
- Android中四大组件
- 一种android安全机制-数字签名