范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

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

  今天面试遇到同学说做过内存优化,于是我一般都会问那 Bitmap 的像素内存存在哪?大多数同学都回答在 java heap 里面,就比较尴尬,理论上你做内存优化,如果连图片这个内存大户内存存在哪都不清楚,实在不太能说得过去。
  Bitmap可以说是安卓里面最常见的内存消耗大户了,我们开发过程中遇到的oom问题很多都是由它引发的。谷歌官方也一直在迭代它的像素内存管理策略。从 Android 2.3.3以前的分配在native上,到2.3-7.1之间的分配在java堆上,又到8.0之后的回到native上。几度变迁,它的回收方法也在跟着变化。  一、Android 2.3.3以前
  2.3.3以前Bitmap的像素内存是分配在natvie上,而且不确定什么时候会被回收。根据 官方文档 的说法我们需要手动调用  Bitmap.recycle()   去回收:
  https://developer.android.com/topic/performance/graphics/manage-memory
  在 Android 2.3.3(API 级别 10)及更低版本上,位图的后备像素数据存储在本地内存中。它与存储在 Dalvik 堆中的位图本身是分开的。本地内存中的像素数据并不以可预测的方式释放,可能会导致应用短暂超出其内存限制并崩溃。
  在 Android 2.3.3(API 级别 10)及更低版本上,建议使用   recycle()   。如果您在应用中显示大量位图数据,则可能会遇到 OutOfMemoryError 错误。利用recycle()    方法,应用可以尽快回收内存。
  注意:只有当您确定位图已不再使用时才应该使用 recycle()。如果您调用recycle() 并在稍后尝试绘制位图,则会收到错误:"Canvas: trying to use a recycled bitmap"。二、Android 3.0~Android 7.1
  虽然3.0~7.1的版本Bitmp的像素内存是分配在java堆上的,但是实际是在natvie层进行decode的,而且会在native层创建一个c++的对象和java层的Bitmap对象进行关联。
  从BitmapFactory的源码我们可以看到它一路调用到 nativeDecodeStream   这个native方法:// BitmapFactory.java public static Bitmap decodeFile(String pathName, Options opts) {     ...     stream = new FileInputStream(pathName);     bm = decodeStream(stream, null, opts);     ...     return bm; }  public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {     ...     bm = decodeStreamInternal(is, outPadding, opts);     ...     return bm; }  private static Bitmap decodeStreamInternal(InputStream is, Rect outPadding, Options opts) {     ...     return nativeDecodeStream(is, tempStorage, outPadding, opts); }
  nativeDecodeStream   实际上会通过jni创建java堆的内存,然后读取io流解码图片将像素数据存到这个java堆内存里面:// BitmapFactory.cpp static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,         jobject padding, jobject options) {     ...     bitmap = doDecode(env, bufferedStream, padding, options);     ...     return bitmap; }  static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) {     ...     // outputAllocator是像素内存的分配器,会在java堆上创建内存给像素数据,可以通过BitmapFactory.Options.inBitmap复用前一个bitmap像素内存     SkBitmap::Allocator* outputAllocator = (javaBitmap != NULL) ?             (SkBitmap::Allocator*)&recyclingAllocator : (SkBitmap::Allocator*)&javaAllocator;     ...     // 将内存分配器设置给解码器     decoder->setAllocator(outputAllocator);     ...     //解码     if (decoder->decode(stream, &decodingBitmap, prefColorType, decodeMode)                 != SkImageDecoder::kSuccess) {         return nullObjectReturn("decoder->decode returned false");     }     ...     return GraphicsJNI::createBitmap(env, javaAllocator.getStorageObjAndReset(),             bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1); }  // Graphics.cpp jobject GraphicsJNI::createBitmap(JNIEnv* env, android::Bitmap* bitmap,         int bitmapCreateFlags, jbyteArray ninePatchChunk, jobject ninePatchInsets,         int density) {      // java层的Bitmap对象实际上是natvie层new出来的     // native层也会创建一个android::Bitmap对象与java层的Bitmap对象绑定     // bitmap->javaByteArray()代码bitmap的像素数据其实是存在java层的byte数组中     jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,             reinterpret_cast(bitmap), bitmap->javaByteArray(),             bitmap->width(), bitmap->height(), density, isMutable, isPremultiplied,             ninePatchChunk, ninePatchInsets);     ...     return obj; }
  我们可以看最后会调用 javaAllocator.getStorageObjAndReset()   创建一个android::Bitmap   类型的native层Bitmap对象,然后通过jni调用java层的Bitmap构造函数去创建java层的Bitmap对象,同时将native层的Bitmap对象保存到mNativePtr:// Bitmap.java // Convenience for JNI access private final long mNativePtr;  /**  * Private constructor that must received an already allocated native bitmap  * int (pointer).  */ // called from JNI Bitmap(long nativeBitmap, byte[] buffer, int width, int height, int density,         boolean isMutable, boolean requestPremultiplied,         byte[] ninePatchChunk, NinePatch.InsetStruct ninePatchInsets) {     ...     mNativePtr = nativeBitmap;     ... }
  从上面的源码我们也能看出来,Bitmap的像素是存在java堆的,所以如果bitmap没有人使用了,垃圾回收器就能自动回收这块的内存,但是在native创建出来的nativeBitmap要怎么回收呢?从Bitmap的源码我们可以看到在Bitmap构造函数里面还会创建一个 BitmapFinalizer   去管理nativeBitmap:/**  * Private constructor that must received an already allocated native bitmap  * int (pointer).  */ // called from JNI Bitmap(long nativeBitmap, byte[] buffer, int width, int height, int density,         boolean isMutable, boolean requestPremultiplied,         byte[] ninePatchChunk, NinePatch.InsetStruct ninePatchInsets) {     ...     mNativePtr = nativeBitmap;     mFinalizer = new BitmapFinalizer(nativeBitmap);     ... }
  BitmapFinalizer   的原理十分简单。Bitmap对象被销毁的时候BitmapFinalizer   也会同步被销毁,然后就可以在BitmapFinalizer.finalize()   里面销毁native层的nativeBitmap:private static class BitmapFinalizer {     private long mNativeBitmap;     ...     BitmapFinalizer(long nativeBitmap) {         mNativeBitmap = nativeBitmap;     }     ...     @Override     public void finalize() {         try {             super.finalize();         } catch (Throwable t) {             // Ignore         } finally {             setNativeAllocationByteCount(0);             nativeDestructor(mNativeBitmap);             mNativeBitmap = 0;         }     } }三、Android 8.0之后
  8.0以后像素内存又被放回了native上,所以依然需要在java层的Bitmap对象回收之后同步回收native的内存。
  虽然 BitmapFinalizer   同样可以实现,但是Java的finalize   方法实际上是不推荐使用的,所以谷歌也换了NativeAllocationRegistry   去实现:/**  * Private constructor that must received an already allocated native bitmap  * int (pointer).  */ // called from JNI Bitmap(long nativeBitmap, int width, int height, int density,         boolean isMutable, boolean requestPremultiplied,     ...     mNativePtr = nativeBitmap;     long nativeSize = NATIVE_ALLOCATION_SIZE + getAllocationByteCount();     NativeAllocationRegistry registry = new NativeAllocationRegistry(         Bitmap.class.getClassLoader(), nativeGetNativeFinalizer(), nativeSize);     registry.registerNativeAllocation(this, nativeBitmap); }
  NativeAllocationRegistry   底层实际上使用了sun.misc.Cleaner   ,可以为对象注册一个清理的Runnable。当对象内存被回收的时候jvm就会调用它。import sun.misc.Cleaner;  public Runnable registerNativeAllocation(Object referent, Allocator allocator) {     ...     CleanerThunk thunk = new CleanerThunk();     Cleaner cleaner = Cleaner.create(referent, thunk);     .. }  private class CleanerThunk implements Runnable {     ...     public void run() {         if (nativePtr != 0) {             applyFreeFunction(freeFunction, nativePtr);         }         registerNativeFree(size);     }     ... }
  这个Cleaner的原理也很暴力,首先它是一个虚引用, registerNativeAllocation   实际上创建了一个Bitmap的虚引用:// Cleaner.java public class Cleaner extends PhantomReference {     ...     public static Cleaner create(Object ob, Runnable thunk) {         ...         return add(new Cleaner(ob, thunk));     }     ...     private Cleaner(Object referent, Runnable thunk) {         super(referent, dummyQueue);         this.thunk = thunk;     }     ...     public void clean() {         ...         thunk.run();         ...     }     ... }
  虚引用的话我们都知道需要配合一个 ReferenceQueue   使用,当对象的引用被回收的时候,jvm就会将这个虚引用丢到ReferenceQueue   里面。而ReferenceQueue   在插入的时候居然通过instanceof   判断了下是不是Cleaner:// ReferenceQueue.java private boolean enqueueLocked(Reference<? extends T> r) {     ...     if (r instanceof Cleaner) {         Cleaner cl = (sun.misc.Cleaner) r;         cl.clean();         ...     }     ... }
  也就是说Bitmap对象被回收,就会触发Cleaner这个虚引用被丢入 ReferenceQueue   ,而ReferenceQueue   里面会判断丢进来的虚引用是不是Cleaner,如果是就调用Cleaner.clean()   方法。而clean   方法内部就会再去执行我们注册的清理的Runnable。最后
  在这里还分享一份由大佬亲自收录整理的学习PDF+架构视频+面试文档+源码笔记,高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料
  这些都是我现在闲暇时还会反复翻阅的精品资料。里面对近几年的大厂面试高频知识点都有详细的讲解。相信可以有效地帮助大家掌握知识、理解原理,帮助大家在未来取得一份不错的答卷。
  当然,你也可以拿去查漏补缺,提升自身的竞争力。
  真心希望可以帮助到大家,Android路漫漫,共勉!
  如果你有需要的话,只需私信我【进阶】即可获取

927世界旅游日,山西景区首波优惠来啦9。27世界旅游日要到啦,山西景区第一波优惠来啦!收藏起来吧!需要注意,不少景区最新防疫政策要求,游客入园须提供48小时核酸阴性证明,进入室内场所须提供24小时核酸阴性证明。出行前CBA3消息威姆斯山西首秀286深圳名帅投奔杜锋广厦54超外退役CBA联赛还有不到半个月时间即将开战,眼下各支球队都在进行最后的磨合与热身工作,十月初即将正式展开季前赛的争夺,也正因为如此原本计划在十月上旬补办的CBA全明星赛再次延期,根据最新山西籍梁强拟任中国信达总裁!晋能控股集团多板块董事高管调整9月23日,中国华融发布公告称,董事会收到公司总裁梁强的辞职函。梁强因工作变动,辞去中国华融执行董事董事会战略发展委员会委员及公司总裁职务,其辞职即日起生效。世界晋商网获悉,9月2家住黄河边,吃上乡村旅游饭来源人民网人民日报黄河边的蓑衣樊村俯瞰。王克军摄(人民视觉)黄新海向顾客展示黄河梭鱼。本报记者李蕊摄泛舟蓑衣樊村湿地。闫立军摄(人民视觉)一条大河波浪宽,风吹稻花香两岸。我家就在岸秋天,山药汤,藕汤不如此汤!鲜美营养健康,助你秋冬少生病俗语有云秋日莫过一碗汤,深秋天气寒凉,人体阳气内敛,寒气上升,寒气易危害健康,此时一碗浓郁温暖的汤水,可以温暖五脏六腑,令你受益无穷。说到秋天的汤,大家时常想到就是莲藕汤,山药汤,独立户外洞穴探索第13集半途放弃开发的松花溶洞专栏分类独立自由旅行户外探索洞穴探险地理位置重庆市巫溪县朝阳镇松花村小众景点松花溶洞拍摄设备智云S4华为智能云华为mate40pro佳能5D温馨提示点击文中蓝色文字可直接欣赏相应视广东男篮迎来CBA少帅!曾携手杜锋执教国家队,2次率队打进季后赛今年夏季休赛期的CBA球员市场并不是特别热闹,球迷们关注的郭艾伦和周琦都没有更换新东家,郭艾伦最终还是与辽篮续约,而周琦也没能如愿地加盟辽篮,只能继续选择到海外联赛发展。因此,今夏杜锋喜上眉梢!广东宏远大外援即将来华,有望赶上第一阶段赛事据知名篮球记者宋翔的最新消息,广东宏远签下的大外援埃利斯目前已经办好工作签证,在选择好航班后即将飞赴国内!这个消息对于广东宏远来说,真的是大喜讯,因为前段时间这名外援的工作签证还未泉州古城中心的老牌酒店出门就是景,而且服务超贴心出门旅游就喜欢住在离景区近,环境又好的酒店店。这次去泉州选择的是泉州老牌的华侨大厦,发现离景区真是超近的。在古城的中心,旁边就是府文庙,在对面是铜佛寺和承天寺,距离关岳庙也很近。文有温泉之名,无活水之实,大碧头之行大碧头度假旅游区算是桂林近年来比较新的景点了,它地处全州县大碧头村,离桂林市区有150公里,高速直通景区,走高速大概两个半小时就可以到达了。景区主打是亲子游和温泉,我们趁着中秋假期普通南京人结婚有多壕?五星级酒店办喜宴,一桌没有5000元下不来南北由于饮食差异环境差异,所以很多生活习惯上都是有所不同的,就拿结婚酒席来说,虽然都很豪横,但是在菜系风格差异上还是很大的,今天就带大家了解一下普通南京人的一场婚礼。南京婚礼举办时
江南水乡的鱼文化节11月18日,和孚镇荻港村村民在展示手工制作蚕丝被的传统技艺。当日,浙江湖州南浔区第十四届鱼文化节在和孚镇荻港村举行,游客看表演品民俗尝鱼味,体验传统丰收场景。一年一度的鱼文化节已胡萝卜叶子别再扔了蒸苦累很美味冬吃萝卜夏吃姜,冬季是适合吃萝卜的季节,萝卜的价格也很便宜。但是好多人在买萝卜的时候,往往把茎叶都踢除了,殊不知其实胡萝卜的叶子也可以制作营养而且简单的美食!下边就讲解用胡萝卜叶子探索申城换锦花绽放,快来公园邂逅Ta们处暑过后,换锦花进入了盛花期,如梦似幻的换锦花在林下绽放。市绿化市容局推荐了上海植物园古猗园等赏花好去处。一起去打卡吧!请大家注意个人防护,不扎堆不聚集,做到防疫三件套,防护五还要航拍宁夏银川贺兰山运动公园从废弃矿坑到美丽自然生态画卷航拍银川市西夏区贺兰山运动公园。孙郑涛摄航拍银川市西夏区贺兰山运动公园。孙郑涛摄航拍银川市西夏区贺兰山运动公园。孙郑涛摄航拍银川市西夏区贺兰山运动公园。孙郑涛摄航拍银川市西夏区贺兰深圳簕杜鹃花展正举办,市民到莲花山公园赏花需提前预约南都讯记者张小玲2022深圳于11月15日12月5日期间举行自花展开展以来吸引了大量市民前往观展,为保证游园秩序,做好疫情防控,给市民游客提供安全有序的观展环境,11月19日12月全体福州人!恢复堂食开放公园后,这些细节千万别大意!本周末天气晴好不少人携家带口到公园玩或者逛街享受美食虽然目前一些公园已经开放堂食也有序恢复但当前疫情防控形势仍然复杂严峻专家提醒在外游玩的时候一定要加强个人防护上图位于乌龙江边的沙(社会)萝卜小镇迎丰收特色产业促振兴近日,位于天津市西青区辛口镇的约6000亩沙窝萝卜陆续迎来丰收。近年来,辛口镇积极调整产业结构,依托沙窝萝卜种植产业,成立销售联盟和农业合作社,举办沙窝萝卜文化旅游节,建设沙窝萝卜头泡茶起白沫不能喝?老茶客分享3个饮茶谣言,看完别再信我国是茶叶故乡,自神农氏以来,茶逐渐发展成为了人们生活中不可缺少的一种饮品。虽然喝茶对人好处多多,但它也是比较讲究的。正因此,一直以来关于喝茶的各种说法,真真假假广泛流传,其中有不手脚凉,却爱上火!讲一张方子,温命火,均寒热入冬以后,很多人的身体有寒热不均的情况,主要表现就是手脚冰凉但是嘴里爱上火,出溃疡长泡。对于这种冰火两重天的情况,中医有对应的解决办法。老规矩,讲一个简单的医案王女士,54岁,什么肾阴虚还是肾阳虚?阴阳两虚怎么办?记好这四味药,阴阳双补大家好,我是李医生!很多人感觉自己腰膝酸软,身体容易疲乏,想要补肾,但是搞不清自己是肾阴虚还是肾阳虚,不知道怎么下手。今天,我们就讲讲怎么样才能更好地补肾!首先,肾阴虚的人是肾里边养肝护肝怎么吃?肝病专家道出大实话1。丝瓜丝瓜中含有丰富的蛋白质,脂肪,粗纤维,碳水化合物等。丝瓜具有非常好的活血化瘀通络的作用,肝硬化正是由于瘀血引起来的。2。佛手佛手具有养肝护肝的功效。大家都知道肝气容易郁气,