内容简介:我们启动一个空壳app,所谓空壳app 就是里面实际上啥都没有,一张图片都没有的app,在我的小米Note 4x上看看内存占用多少我曹 怎么会有 36mb? wtf? 这个graphics里面几乎都是bitmap,为何这里bitmap占用内存这么多呀?我明明一张图都没加载啊。
我们启动一个空壳app,所谓空壳app 就是里面实际上啥都没有,一张图片都没有的app,在我的小米Note 4x上看看内存占用多少
我曹 怎么会有 36mb? wtf? 这个graphics里面几乎都是bitmap,为何这里bitmap占用内存这么多呀?
我明明一张图都没加载啊。
打开内存dump,导出以后 用hprof工具转换成mat可以解析的格式:
最后用mat打开,搜下bitmap 看看是啥?
嗯?怎么有这么多bitmap对象?我没加载过任何图片啊?
查查看都是谁引用的?
去源码里面查查这是个 sPreloadedDrawables 啥东西?
既然是跟资源有关的东西 肯定绕不开ResourcesImpl这个类了,很快就能搜到
实际上解释起来,就是android系统启动的时候肯定是先启动zygote进程,有了这个进程以后,其他app进程启动 就只要fork他即可。所以实际上当zygote进程启动的时候会把一些系统的资源优先加载到内存里, 注意 这个framework本身包含的资源是很多的,但是只有很有限的资源才有资格到这预加载内存里面
所以当我们zygote进程启动完毕以后,这个进程的内存里面就有这些 系统资源在里面了,我们这里只关注bitmap资源 也就是drawable,然后其他app进程启动的时候既然是fork了zygote进程,自然而然的这里 也会包含这些图片资源。
这就是为什么我们一个空壳app也会加载那么多bitmap在内存里的原因, 这样做的好处当然是如果你的app用到了这些预加载 的资源,那么可以省略decode的过程,直接从内存里面取,速度快,缺点就是这个东西占用的内存着实有一些大了。。。 动不动20mb左右的空间。
我们来看下系统loadDrawable的过程就可以加深这个理解了
@Nullable Drawable loadDrawable(Resources wrapper, TypedValue value, int id, Resources.Theme theme, boolean useCache) throws NotFoundException { try { if (TRACE_FOR_PRELOAD) { // Log only framework resources if ((id >>> 24) == 0x1) { final String name = getResourceName(id); if (name != null) { Log.d("PreloadDrawable", name); } } } final boolean isColorDrawable; final DrawableCache caches; final long key; if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && value.type <= TypedValue.TYPE_LAST_COLOR_INT) { isColorDrawable = true; caches = mColorDrawableCache; key = value.data; } else { isColorDrawable = false; caches = mDrawableCache; key = (((long) value.assetCookie) << 32) | value.data; } // First, check whether we have a cached version of this drawable // that was inflated against the specified theme. Skip the cache if // we're currently preloading or we're not using the cache. if (!mPreloading && useCache) { final Drawable cachedDrawable = caches.getInstance(key, wrapper, theme); if (cachedDrawable != null) { return cachedDrawable; } } // Next, check preloaded drawables. Preloaded drawables may contain // unresolved theme attributes. final Drawable.ConstantState cs; if (isColorDrawable) { cs = sPreloadedColorDrawables.get(key); } else { //这里看到没有 取系统缓存了 cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key); } //取不到就重新decode Drawable dr; if (cs != null) { dr = cs.newDrawable(wrapper); } else if (isColorDrawable) { dr = new ColorDrawable(value.data); } else { dr = loadDrawableForCookie(wrapper, value, id, null); } // Determine if the drawable has unresolved theme attributes. If it // does, we'll need to apply a theme and store it in a theme-specific // cache. final boolean canApplyTheme = dr != null && dr.canApplyTheme(); if (canApplyTheme && theme != null) { dr = dr.mutate(); dr.applyTheme(theme); dr.clearMutated(); } // If we were able to obtain a drawable, store it in the appropriate // cache: preload, not themed, null theme, or theme-specific. Don't // pollute the cache with drawables loaded from a foreign density. if (dr != null && useCache) { dr.setChangingConfigurations(value.changingConfigurations); cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr); } return dr; } catch (Exception e) { String name; try { name = getResourceName(id); } catch (NotFoundException e2) { name = "(missing name)"; } // The target drawable might fail to load for any number of // reasons, but we always want to include the resource name. // Since the client already expects this method to throw a // NotFoundException, just throw one of those. final NotFoundException nfe = new NotFoundException("Drawable " + name + " with resource ID #0x" + Integer.toHexString(id), e); nfe.setStackTrace(new StackTraceElement[0]); throw nfe; } } 复制代码
所以这里就给我们提供了一种优化思路:
当我们的app刚开始运行时,内存富余空间很大,所以我们可以不关心这个系统缓存带来的问题,但是如果当我们发现 内存压力变大的时候,就可以考虑在合适的时机,手动的释放掉这20mb左右的系统缓存了,释放的方法也很简单 反射clear一下即可:
Resources resource = getApplicationContext().getResources(); try { //注意这个地方 有些rom会有独特的名字,需要你们线上监控到不同rom以后 遍历下他们的field看看这个变量 //的名字是什么 才能hook成功 Field field = Resources.class.getDeclaredField("sPreloadedDrawables"); field.setAccessible(true); LongSparseArray<Drawable.ConstantState>[] sPreloadedDrawables = (LongSparseArray<Drawable.ConstantState>[]) field .get(resource); for (LongSparseArray<Drawable.ConstantState> s : sPreloadedDrawables) { s.clear(); } } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } 复制代码
当然这样的开销也有弊病,就是如果你释放掉这部分内存以后,如果要加载这些资源,那么会多一步decode的过程。
当然现在插件框架很多了,很多人都对resources这部分有了解,其实这个地方我们完全可以利用动态代理的方法 hook掉 系统的这个取缓存的sPreloadedDrawables 过程,让他们取 我们自己图片加载框架里的缓存即可。 但是注意这个hook的过程要考虑不同rom的兼容性的地方就更多了,有兴趣的同学可以自行研究下。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- opencv自带例子学习-灰度图像的阈值操作
- AI+时代,浅谈产品经理对图像识别技术的阈值控制
- BLS算法在加密货币上的应用:秘钥共享与阈值签名
- 强随机数应用链链RandDrop已上线——BLS签名实现阈值签名的流程
- zabbix监控tomcat 自定义监控项
- iOS 性能监控(一)—— CPU功耗监控
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。