Android当内存监控到阈值时应该怎么办?

栏目: IOS · Android · 发布时间: 5年前

内容简介:我们启动一个空壳app,所谓空壳app 就是里面实际上啥都没有,一张图片都没有的app,在我的小米Note 4x上看看内存占用多少我曹 怎么会有 36mb? wtf? 这个graphics里面几乎都是bitmap,为何这里bitmap占用内存这么多呀?我明明一张图都没加载啊。

我们启动一个空壳app,所谓空壳app 就是里面实际上啥都没有,一张图片都没有的app,在我的小米Note 4x上看看内存占用多少

Android当内存监控到阈值时应该怎么办?

我曹 怎么会有 36mb? wtf? 这个graphics里面几乎都是bitmap,为何这里bitmap占用内存这么多呀?

我明明一张图都没加载啊。

打开内存dump,导出以后 用hprof工具转换成mat可以解析的格式:

Android当内存监控到阈值时应该怎么办?

最后用mat打开,搜下bitmap 看看是啥?

Android当内存监控到阈值时应该怎么办?

嗯?怎么有这么多bitmap对象?我没加载过任何图片啊?

查查看都是谁引用的?

Android当内存监控到阈值时应该怎么办?

去源码里面查查这是个 sPreloadedDrawables 啥东西?

既然是跟资源有关的东西 肯定绕不开ResourcesImpl这个类了,很快就能搜到

Android当内存监控到阈值时应该怎么办?

实际上解释起来,就是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的兼容性的地方就更多了,有兴趣的同学可以自行研究下。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

如何构建敏捷项目管理团队

如何构建敏捷项目管理团队

丽萨·阿金斯 / 徐蓓蓓、白云峰、刘江华 / 电子工业出版社 / 2012-6 / 49.00元

《敏捷项目管理系列丛书•PMI-ACPSM考试指定教材•如何构建敏捷项目管理团队:ScrumMaster、敏捷教练与项目经理的实用指南》结合作者的亲身经历告诉读者如何建立一个高性能的敏捷项目管理团队,以及最终成为一名优秀的敏捷教练。作者将敏捷教练定义为导师、协助者、老师、问题解决者、冲突领航员、协作指挥者,正是这种不同角色之间的细微区别才使敏捷教练的工作富有深度。《敏捷项目管理系列丛书•PMI-A......一起来看看 《如何构建敏捷项目管理团队》 这本书的介绍吧!

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

html转js在线工具
html转js在线工具

html转js在线工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具