是否需要主动调用Bitmap的recycle方法
一个Bitmap使用完后,是只需要等它成为垃圾后让GC去回收,还是应该主动调用recycle方法呢?或者说,主动调用recycle方法是否有好处,是否能马上回收内存呢?
带着这个问题来看源码(我看的4.4源码)。
先看Bitmap内存的创建,通过跟踪Bitmap.createBitmap方法,可以发现是native方法里调用的JVM来创建的:
jbyteArray arrayObj = env->NewByteArray(size);
native使用的是通过其得到的一个固定地址:
jbyte* addr = jniGetNonMovableArrayElements(&env->functions, arrayObj);
native里会用一个SkPixelRef来存放他们:
SkPixelRef* pr = new AndroidPixelRef(env, bitmapInfo, (void*) addr, bitmap->rowBytes(), arrayObj, ctable);
然后将这个传给Bitmap:
bitmap->setPixelRef(pr);
再看recycle过程:
java端:
mBuffer = null;
native端:
Caches::getInstance().textureCache.removeDeferred(bitmap);
fPixelRef->unref(); // fPixelRef是上面分配的SkPixelRef
fPixelRef = NULL;
这里其实就是java端将mBuffer置为垃圾。native端释放SkPixelRef,并延迟删除其对应的TextureCache(最终的删除应该是在下一帧开始前)。
再看Bitmap的finalize,发现Bitmap类自己没有finalize,专门用了一个静态内部类BitmapFinalizer,其finalize方法来做native资源的释放。至于为什么要这么弄,我后面另说。
其会调用到native里:
Caches::getInstance().textureCache.removeDeferred(resource);
delete resource;
延迟删除其对应的TextureCache,并删除SkBitmap。
从这么来看,recycle方法会释放部分native内存,但并不会释放Bitmap占用内存最大的图像数据内存。
但我突然想到,好像截屏时得到的Bitmap的图像数据内存并不是在JVM里申请的,查看代码,果然是这样。其并不是通过Bitmap.createBitmap方法创建的图像:
GraphicsJNI::createBitmap(env, bitmap, GraphicsJNI::kBitmapCreateFlag_Premultiplied, NULL);
其使用的SkPixelRef也不是上面的AndroidPixelRef,而是ScreenshotPixelRef,里面持有着图像数据。
在这种情况下,调用recycle方法是会释放其图像数据的。
另外要命的是,假如我们不停截屏并丢掉之前的Bitmap,我们可能觉得很容易就会有垃圾回收,那么之前的Bitmap就回收了。可是由于Bitmap的图像数据才是内存大户,Bitmap本身占用内存非常小,因此这种情况下Bitmap的构造引起垃圾回收的可能性很低。
我做了个试验,在app里点击按钮截一次屏,然后马上扔掉,使其为垃圾。我不停点击按钮,最终系统内存耗尽导致app被杀,也没有发生GC。改为每次截屏后手动调用gc,就不会导致内存增大。
并且危险的是,这部分内存并不是分配在app端,就算app被杀也不会释放。(截屏的内存是在SurfaceFlinger端申请的,app端的释放应该只是把内存使用权还给SurfaceFlinger,SurfaceFlinger会继续重用它,但不会彻底释放还给系统,因此变大之后不会变小,不清楚有没有最终的释放逻辑。我以前在做系统截屏的时候曾因为没有主动recycle导致占用极大系统内存。)
画个表格来说明一下recycle在各种情况下会回收哪些内存吧:
|
|
SkPixelRef (小) |
SkBitmap (小) |
图像数据 (面积大则很大) |
TextureCache (同图像数据相当) |
|---|---|---|---|---|
|
JVM中分配图像数据如Bitmap.createBitmap 且没有被硬件加速draw过 |
√ | × | × | 无此内存 |
|
JVM中分配图像数据如Bitmap.createBitmap 且有被硬件加速draw过 |
√ | × | × | √ |
|
不在JVM中分配图像数据如截屏 |
√ | × | √ | 情况同上 |
这些内存在其Bitmap成为垃圾后的垃圾回收过程里都会释放。但是,在native内存吃紧的情况下系统是不知道可以通过GC来回收一部分native内存的,所以尽早释放是有积极作用的。
结论:尽快的调用recycle是个好习惯,会释放与其相关的native分配的内存;但一般情况下其图像数据是在JVM里分配的,调用recycle并不会释放这部分内存。
我们用createBitmap创建的Bitmap且没有被硬件加速Canvas draw过,则主动调用recycle产生的意义比较小,仅释放了native里的SkPixelRef的内存,这种情况我觉得可以不主动调用recycle。
被硬件加速Canvas draw过的由于有TextureCache应该尽快调用recycle来尽早释放其TextureCache。
像截屏这种不是在JVM里分配内存的情况也应该尽快调用recycle来马上释放其图像数据。
(一个例外,如果是通过Resources.getDrawable得到的Bitmap,不应该调用recycle,因为它可能会被重用)
另,说一下上面提到的Bitmap的finalize实现方式。
这里没有直接在Bitmap上实现finalize,而是用一个静态内部类专门实现finalize,这是因为在GC过程中,没有finalize的对象可以直接回收,而有finalize的对象需要多保持一会儿来执行其finalize方法,然后才能回收,在我以前了解的C#的垃圾回收机制里,有finalize的对象在第一次GC的时候只会执行其finalize方法,要到下一次GC才会回收其内存。所以Bitmap的这种finalize实现方式是为了让占用内存大的部分(Bitmap类)没有finalize,可以早点释放;BitmapFinalizer内部类仅持有一个NativeBitmap指针,通过finalize去释放native内存。这样最有效的达到既提前释放主要内存又能通过finalize释放native内存的目的。
是否需要主动调用Bitmap的recycle方法的更多相关文章
- mui---子页面主动调用父页面的方法
我们在做APP的时候,很多时候会有这样的功能需求,例如:登录,充值,如果登录成功,或充值成功后,需要更改当前页面以及父页面的状态信息,就会用到在子页面调用父页面的方法来实现:在子页面刷新父页面的功能. ...
- vue--父组件主动获取子组件的方法
父组件主动获取子组件的方法和属性 第一步:调用自组件的时候,给自组建定义一个Header <v-header ref='headerInfo'></v-header> 第二步: ...
- 一、Bitmap的recycle问题
尽管Android有自己的垃圾回收机制,对于是不是要我们自己调用recycle,还的看情况而定.假设仅仅是使用少量的几张图片,回收与否关系不大.但是若有大量bitmap须要垃圾回收处理,那必定垃圾回收 ...
- Bitmap的recycle问题
虽然Android有自己的垃圾回收机制,对于是不是要我们自己调用recycle,还的看情况而定.如果只是使用少量的几张图片,回收与否关系不大.可是若有大量bitmap需要垃圾回收处理,那必然垃 ...
- Android处理Bitmap的一些方法
http://www.it165.net/pro/html/201305/5795.html # 文件与Bitmap间的方法 1. 从文件载入Bitmap 01./** 02.* @brief 从文件 ...
- react中直接调用子组件的方法(非props方式)
我们都知道在 react中,若要在父组件调用子组件的方法,通常我们会采用在父组件定义一个方法,作为props转给子组件,然后执行该方法,可以获取到子组件传回的参数以得到我们的目的. 显而易见,这个执行 ...
- 【vue】父组件主动调用子组件 /// 非父子组件传值
一 父组件主动调用子组件: 注意:在父组件使用子组件的标签上注入ref属性,例如: <div id="home"> <v-header ref="he ...
- Android 调用系统相机拍照保存以及调用系统相册的方法
系统已经有的东西,如果我们没有新的需求的话,直接调用是最直接的.下面讲讲调用系统相机拍照并保存图片和如何调用系统相册的方法. 首先看看调用系统相机的核心方法: Intent camera = new ...
- oracle调用JAVA类的方法
导入jar包 在oracle中导入需要的jar包,我们把编辑好的java类打成jar包,直接在oarcle里面写简单的调用就可以了, 1.操作系统需要拥有支持loadjava命令的jdk. 2.加 ...
随机推荐
- XFire构建web service客户端的五种方式
这里并未涉及到JSR 181 Annotations 的相关应用,具体的三种方式如下 ① 通过WSDL地址来创建动态客户端 ② 通过服务端提供的接口来创建客户端 ③ 使用Ant通过WSDL文件来生成客 ...
- Spring MVC 3.0 深入及对注解的详细讲解[转载]
http://blog.csdn.net/jzhf2012/article/details/8463783 核心原理 1. 用户发送请求给服务器.url:user.do 2. ...
- 用exec调用带有output输出参数的存储过程
用exec调用带有output输出参数的存储过程,想要得到输出参数的值,必须在调用参数后面加output关键字,如: declare @value int exec up_test 2,3,@v ...
- 利用百度翻译API,获取翻译结果
利用百度翻译API,获取翻译结果 translate.py #!/usr/bin/python #-*- coding:utf-8 -*- import sys reload(sys) sys.set ...
- C#后台调用前台javascript的五种方法小结
第一种,OnClientClick (vs2003不支持这个方法) <asp:Button ID="Button1" runat="server" Tex ...
- 自己写的jQuery放大镜插件效果(二)(采用只有一张图片的思路)
废话不多说,先看效果图,和上一章节用的是同一个小图片: 这个方法实现的放大有个弊端就是放大倍数设置的过大的话,会带来图片上的模糊.但是图片加载的话要比使用2张图片加载的快很多 插件代码: ; (fun ...
- boost实现日期时间格式化
#include <iostream> #include<thread> #include<chrono> #include<clocale> #inc ...
- 伯克利、OpenAI等提出基于模型的元策略优化强化学习
基于模型的强化学习方法数据效率高,前景可观.本文提出了一种基于模型的元策略强化学习方法,实践证明,该方法比以前基于模型的方法更能够应对模型缺陷,还能取得与无模型方法相近的性能. 引言 强化学习领域近期 ...
- 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #18 向ext4转换
HACK #18 向ext4转换 ext4可以与ext2/ext3在后台进行互换.这里将介绍从ext2/ext3转换的方法以及转换时的注意事项.转换有两种方法可以将ext2/ext3的磁盘映像作为ex ...
- 根据inode编号来删除文件或目录
在Linux系统上,有时候会出现文件名为特殊字符的文件或目录,当我们使用rm来删除这样的文件或目录时,就会出错导致删不掉.但是我们可以依据inode号来删除这样的文件,方法如下: (1)执行ls -i ...