城市直播房产教育博客汽车
投稿投诉
汽车报价
买车新车
博客专栏
专题精品
教育留学
高考读书
房产家居
彩票视频
直播黑猫
投资微博
城市上海
政务旅游

作为Android开发,这个知识点一定要知道,官方也改了2次

2月18日 鬼神氏投稿
  今天面试遇到同学说做过内存优化,于是我一般都会问那Bitmap的像素内存存在哪?大多数同学都回答在javaheap里面,就比较尴尬,理论上你做内存优化,如果连图片这个内存大户内存存在哪都不清楚,实在不太能说得过去。
  Bitmap可以说是安卓里面最常见的内存消耗大户了,我们开发过程中遇到的oom问题很多都是由它引发的。谷歌官方也一直在迭代它的像素内存管理策略。从Android2。3。3以前的分配在native上,到2。37。1之间的分配在java堆上,又到8。0之后的回到native上。几度变迁,它的回收方法也在跟着变化。一、Android2。3。3以前
  2。3。3以前Bitmap的像素内存是分配在natvie上,而且不确定什么时候会被回收。根据官方文档的说法我们需要手动调用Bitmap。recycle()去回收:
  https:developer。android。comtopicperformancegraphicsmanagememory
  在Android2。3。3(API级别10)及更低版本上,位图的后备像素数据存储在本地内存中。它与存储在Dalvik堆中的位图本身是分开的。本地内存中的像素数据并不以可预测的方式释放,可能会导致应用短暂超出其内存限制并崩溃。
  在Android2。3。3(API级别10)及更低版本上,建议使用recycle()。如果您在应用中显示大量位图数据,则可能会遇到OutOfMemoryError错误。利用recycle()方法,应用可以尽快回收内存。
  注意:只有当您确定位图已不再使用时才应该使用recycle()。如果您调用recycle()并在稍后尝试绘制位图,则会收到错误:Canvas:tryingtousearecycledbitmap。二、Android3。0Android7。1
  虽然3。07。1的版本Bitmp的像素内存是分配在java堆上的,但是实际是在natvie层进行decode的,而且会在native层创建一个c的对象和java层的Bitmap对象进行关联。
  从BitmapFactory的源码我们可以看到它一路调用到nativeDecodeStream这个native方法:BitmapFactory。javapublicstaticBitmapdecodeFile(StringpathName,Optionsopts){。。。streamnewFileInputStream(pathName);bmdecodeStream(stream,null,opts);。。。}publicstaticBitmapdecodeStream(InputStreamis,RectoutPadding,Optionsopts){。。。bmdecodeStreamInternal(is,outPadding,opts);。。。}privatestaticBitmapdecodeStreamInternal(InputStreamis,RectoutPadding,Optionsopts){。。。returnnativeDecodeStream(is,tempStorage,outPadding,opts);}
  nativeDecodeStream实际上会通过jni创建java堆的内存,然后读取io流解码图片将像素数据存到这个java堆内存里面:BitmapFactory。cppstaticjobjectnativeDecodeStream(JNIEnvenv,jobjectclazz,jobjectis,jbyteArraystorage,jobjectpadding,jobjectoptions){。。。bitmapdoDecode(env,bufferedStream,padding,options);。。。}staticjobjectdoDecode(JNIEnvenv,SkStreamRewindablestream,jobjectpadding,jobjectoptions){。。。outputAllocator是像素内存的分配器,会在java堆上创建内存给像素数据,可以通过BitmapFactory。Options。inBitmap复用前一个bitmap像素内存SkBitmap::AllocatoroutputAllocator(javaBitmap!NULL)?(SkBitmap::Allocator)recyclingAllocator:(SkBitmap::Allocator)javaA。。。将内存分配器设置给解码器decodersetAllocator(outputAllocator);。。。解码if(decoderdecode(stream,decodingBitmap,prefColorType,decodeMode)!SkImageDecoder::kSuccess){returnnullObjectReturn(decoderdecodereturnedfalse);}。。。returnGraphicsJNI::createBitmap(env,javaAllocator。getStorageObjAndReset(),bitmapCreateFlags,ninePatchChunk,ninePatchInsets,1);}Graphics。cppjobjectGraphicsJNI::createBitmap(JNIEnvenv,android::Bitmapbitmap,intbitmapCreateFlags,jbyteArrayninePatchChunk,jobjectninePatchInsets,intdensity){java层的Bitmap对象实际上是natvie层new出来的native层也会创建一个android::Bitmap对象与java层的Bitmap对象绑定bitmapjavaByteArray()代码bitmap的像素数据其实是存在java层的byte数组中jobjectobjenvNewObject(gBitmapclass,gBitmapconstructorMethodID,reinterpretcastjlong(bitmap),bitmapjavaByteArray(),bitmapwidth(),bitmapheight(),density,isMutable,isPremultiplied,ninePatchChunk,ninePatchInsets);。。。}
  我们可以看最后会调用javaAllocator。getStorageObjAndReset()创建一个android::Bitmap类型的native层Bitmap对象,然后通过jni调用java层的Bitmap构造函数去创建java层的Bitmap对象,同时将native层的Bitmap对象保存到mNativePtr:Bitmap。javaConvenienceforJNIaccessprivatefinallongmNativePPrivateconstructorthatmustreceivedanalreadyallocatednativebitmapint(pointer)。calledfromJNIBitmap(longnativeBitmap,byte〔〕buffer,intwidth,intheight,intdensity,booleanisMutable,booleanrequestPremultiplied,byte〔〕ninePatchChunk,NinePatch。InsetStructninePatchInsets){。。。mNativePtrnativeB。。。}
  从上面的源码我们也能看出来,Bitmap的像素是存在java堆的,所以如果bitmap没有人使用了,垃圾回收器就能自动回收这块的内存,但是在native创建出来的nativeBitmap要怎么回收呢?从Bitmap的源码我们可以看到在Bitmap构造函数里面还会创建一个BitmapFinalizer去管理nativeBitmap:Privateconstructorthatmustreceivedanalreadyallocatednativebitmapint(pointer)。calledfromJNIBitmap(longnativeBitmap,byte〔〕buffer,intwidth,intheight,intdensity,booleanisMutable,booleanrequestPremultiplied,byte〔〕ninePatchChunk,NinePatch。InsetStructninePatchInsets){。。。mNativePtrnativeBmFinalizernewBitmapFinalizer(nativeBitmap);。。。}
  BitmapFinalizer的原理十分简单。Bitmap对象被销毁的时候BitmapFinalizer也会同步被销毁,然后就可以在BitmapFinalizer。finalize()里面销毁native层的nativeBitmap:privatestaticclassBitmapFinalizer{privatelongmNativeB。。。BitmapFinalizer(longnativeBitmap){mNativeBitmapnativeB}。。。Overridepublicvoidfinalize(){try{super。finalize();}catch(Throwablet){Ignore}finally{setNativeAllocationByteCount(0);nativeDestructor(mNativeBitmap);mNativeBitmap0;}}}三、Android8。0之后
  8。0以后像素内存又被放回了native上,所以依然需要在java层的Bitmap对象回收之后同步回收native的内存。
  虽然BitmapFinalizer同样可以实现,但是Java的finalize方法实际上是不推荐使用的,所以谷歌也换了NativeAllocationRegistry去实现:Privateconstructorthatmustreceivedanalreadyallocatednativebitmapint(pointer)。calledfromJNIBitmap(longnativeBitmap,intwidth,intheight,intdensity,booleanisMutable,booleanrequestPremultiplied,。。。mNativePtrnativeBlongnativeSizeNATIVEALLOCATIONSIZEgetAllocationByteCount();NativeAllocationRegistryregistrynewNativeAllocationRegistry(Bitmap。class。getClassLoader(),nativeGetNativeFinalizer(),nativeSize);registry。registerNativeAllocation(this,nativeBitmap);}
  NativeAllocationRegistry底层实际上使用了sun。misc。Cleaner,可以为对象注册一个清理的Runnable。当对象内存被回收的时候jvm就会调用它。importsun。misc。CpublicRunnableregisterNativeAllocation(Objectreferent,Allocatorallocator){。。。CleanerThunkthunknewCleanerThunk();CleanercleanerCleaner。create(referent,thunk);。。}privateclassCleanerThunkimplementsRunnable{。。。publicvoidrun(){if(nativePtr!0){applyFreeFunction(freeFunction,nativePtr);}registerNativeFree(size);}。。。}
  这个Cleaner的原理也很暴力,首先它是一个虚引用,registerNativeAllocation实际上创建了一个Bitmap的虚引用:Cleaner。javapublicclassCleanerextendsPhantomReference{。。。publicstaticCleanercreate(Objectob,Runnablethunk){。。。returnadd(newCleaner(ob,thunk));}。。。privateCleaner(Objectreferent,Runnablethunk){super(referent,dummyQueue);this。}。。。publicvoidclean(){。。。thunk。run();。。。}。。。}
  虚引用的话我们都知道需要配合一个ReferenceQueue使用,当对象的引用被回收的时候,jvm就会将这个虚引用丢到ReferenceQueue里面。而ReferenceQueue在插入的时候居然通过instanceof判断了下是不是Cleaner:ReferenceQueue。javaprivatebooleanenqueueLocked(R?extendsTr){。。。if(rinstanceofCleaner){Cleanercl(sun。misc。Cleaner)r;cl。clean();。。。}。。。}
  也就是说Bitmap对象被回收,就会触发Cleaner这个虚引用被丢入ReferenceQueue,而ReferenceQueue里面会判断丢进来的虚引用是不是Cleaner,如果是就调用Cleaner。clean()方法。而clean方法内部就会再去执行我们注册的清理的Runnable。最后
  在这里还分享一份由大佬亲自收录整理的学习PDF架构视频面试文档源码笔记,高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料
  这些都是我现在闲暇时还会反复翻阅的精品资料。里面对近几年的大厂面试高频知识点都有详细的讲解。相信可以有效地帮助大家掌握知识、理解原理,帮助大家在未来取得一份不错的答卷。
  当然,你也可以拿去查漏补缺,提升自身的竞争力。
  真心希望可以帮助到大家,Android路漫漫,共勉!
  如果你有需要的话,只需私信我【进阶】即可获取
投诉 评论 转载

中超又尴尬了!保级组球队比争冠组球队分数还要高去年中超因为天津津门虎(当时名称天津泰达)一个赛季赢一场成功保级的奇葩赛制被吐槽。当然中国足坛联赛奇葩的事情一直就有很多,比如大连毅腾(现在名称绍兴柯桥越甲)就因为著名的69事……Epic冬促喜加一!第五款游戏免费领循环英雄Epic游戏商城冬促已经开启!本次Epic平台迄今为止最大的一次特卖,大作都是白菜价,此外,Epic还会连续15天每天都送出一款游戏。今天是第五天,第一款是《莎木3》,第二款是……奇才疯了?向火箭寻求3换1交易,愿送走哈勒尔首轮签,火箭笑了根据《华盛顿报》报道奇才已经决定对队内主要球员进行交易,作为球队的老大比尔遭遇似乎跟利拉德一样,个人有不错的表现,可是球队成绩一直不理想,更别说夺冠了,奇才已经准备好了续约合同……盘点10款热门恐龙游戏,方块方舟镰刀龙几乎什么都能干人类驯服动物已经有千年的历史了,事实也证明动物们可以在农业、战争中发挥巨大的作用。那么如果人类驯服的是已经消逝了的恐龙会发生什么事情呢?为此下面这10款恐龙题材的游戏便给出了答……性感美女时尚写真清纯漂亮清新可爱小姐姐甜美迷人第四十期敬爱的读者,谢谢一路上有你!一直在你们的支持下,我学会了坚强!因为有你,我不怕一切困难的前进。生活,好像一直都在告诉我:活着,不是为了什么,而是因为有了一些人的陪伴……从苹果M1Max回顾史上那些巨无霸级的处理器来源:technews(台)英特尔这家世界第一半导体大厂,过去几十年来,都习惯靠制程技术优势与庞大产能辗压竞争对手,时过境迁,随着专业晶圆代工商业模式崛起,现在反倒变成双……不用光刻机,中国照样造5纳米芯片?雕版印刷术能否突破壁垒图为光刻机目前中国在半导体领域内受到了美国的制裁,国内多家科技企业都遭到了影响,主要原因是中国还没能自行研发出高性能光刻机,但前不久一个好消息传来,国内一个科研团队研究出……INS爆火情侣近况曝光,500万人在线围观生完孩子后,一切都提问:有人知道今年娱乐圈离了几对吗?粗略算了算,十只手指不够掰的。年初的赵丽颖,年末的大S、李湘、王力宏桩桩件件都是轰动娱乐圈的大事。忍不住又想问:真正的爱情……王者荣耀S26战令奖励揭晓,永久皮肤六选一,战令星元特效曝光王者荣耀S26赛季将在下周开启,与新赛季相关的版本内容正在逐步曝光中,而今天则揭晓了S26战令奖励,其中包括三款战令皮肤、同主题的四件套,以及永久皮肤英雄的免费奖励。下面一起来……作为Android开发,这个知识点一定要知道,官方也改了2次今天面试遇到同学说做过内存优化,于是我一般都会问那Bitmap的像素内存存在哪?大多数同学都回答在javaheap里面,就比较尴尬,理论上你做内存优化,如果连图片这个内存大户内……中性名,足协应该扯下的遮羞布2021赛季伊始,中国足协颁布了中性名实施政策,一众球队苦不堪言,投资人心气全无,中国足球大环境一片凄凉。纵观世界足坛,皇家马德里、拜仁慕尼黑、曼彻斯特联、AC米兰经典球……长续航才是王道!这3款5000mAh超大电池手机,不用为电量手机如今已经成为我们生活中最重要的工具,所以拥有一款长续航的手机成为了很多机友的诉求!今天就来推荐几款5000mAh的手机,不仅使用起来没有电量焦虑,甚至最低千元就可以拥有!R……
如何有效提高小学英语阅读教学质量长直发打造清新校园美女甜美校园女生直发推荐浅析事业单位档案规范化管理及优化策略姚明出手保护李月汝,WNBA美国冠军却倒打一耙,给中国女篮泼信息系统造句用信息系统造句大全往来造句用往来造句大全没有作业的日子云南闭壳龟好养吗怎么养青啤怎么区分一厂二厂三厂看瓶身喷码第二排不同厂的房地产项目收并购财务尽调与分析方法全国乒乓球锦标赛黄石开拍孩子的注意力差的原因是什么该如何提升呢
【歌词】M80showtime歌手:M80热文聚热点网 喜出望外川岛小鸟:带着未来上路美术教学计划跨境电商掰腕子:SHEIN、Temu、速卖通各展其长热文聚热 文件怎么打开怎么将转治耳鸣的古方,直接给方!临床效果显著!建。。。热文聚热点网 一夜9大交易动态!沃尔点赞加盟湖人流言,国王退出西蒙斯争夺战排名亚洲宝藏旅行地第一名的甘肃,究竟有多惊艳?来过不愿走最佳瘦腿运动是什么怎么查老公酒店入住记录(老公出轨开宾馆怎么查)白酒到底会不会过期?存了10年的白酒还能喝吗?提前了解不吃亏

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找江西南阳嘉兴昆明铜陵滨州广东西昌常德梅州兰州阳江运城金华广西萍乡大理重庆诸暨泉州安庆南充武汉辽宁