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


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

查看所有标签

猜你喜欢:

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

图解设计模式

图解设计模式

结城浩 / 杨文轩 / 人民邮电出版社 / 2017-1-1 / CNY 79.00

原版连续畅销12年、重印25次! 194张图表 + Java示例代码 = 轻松理解GoF的23种设计模式 《程序员的数学》《数学女孩》作者结城浩又一力作 ◆图文并茂 194张图表(包括57张UML类图)穿插文中,帮助理解各设计模式 ◆通俗易懂 用浅显的语言逐一讲解23种设计模式,读完此书会发现GoF书不再晦涩难懂 ◆专业实用 编写了Java程序代码来......一起来看看 《图解设计模式》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具