博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android当内存监控到阈值时应该怎么办?
阅读量:6268 次
发布时间:2019-06-22

本文共 5711 字,大约阅读时间需要 19 分钟。

我们启动一个空壳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
[] sPreloadedDrawables = (LongSparseArray
[]) field .get(resource); for (LongSparseArray
s : sPreloadedDrawables) { s.clear(); } } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); }复制代码

当然这样的开销也有弊病,就是如果你释放掉这部分内存以后,如果要加载这些资源,那么会多一步decode的过程。

当然现在插件框架很多了,很多人都对resources这部分有了解,其实这个地方我们完全可以利用动态代理的方法 hook掉 系统的这个取缓存的sPreloadedDrawables 过程,让他们取 我们自己图片加载框架里的缓存即可。 但是注意这个hook的过程要考虑不同rom的兼容性的地方就更多了,有兴趣的同学可以自行研究下。

转载地址:http://rgppa.baihongyu.com/

你可能感兴趣的文章
Struts2技术详解
查看>>
MFC应用程序向导生成的文件
查看>>
Oracle体系结构之oracle密码文件管理
查看>>
【leetcode】Remove Element (easy)
查看>>
mysql多表查询及其 group by 组内排序
查看>>
alsa的snd_pcm_readi()函数和snd_pcm_writei()
查看>>
Android学习网站推荐(转)
查看>>
嵌入式根文件系统的移植和制作详解
查看>>
MEF部件的生命周期(PartCreationPolicy)
查看>>
LCD的接口类型详解
查看>>
nginx 基础文档
查看>>
LintCode: Unique Characters
查看>>
Jackson序列化和反序列化Json数据完整示例
查看>>
.net 中的DllImport
查看>>
nyoj 517 最小公倍数 【java睑板】
查看>>
include与jsp:include区别
查看>>
ftp的20 21端口和主动被动模式
查看>>
MySQL存储引擎选型
查看>>
Java中的statickeyword具体解释
查看>>
Linux车载系统的开发方向
查看>>