android内存分析

接上篇,发现应用崩溃是因为OutOfMemory错误,下一步需要分析应用内存,找出哪里有内存泄露。

首先在eclipse中安装android内存分析工具MAT插件。

DDMS查看应用内存分配

  • 打开DDMS,点击“Update Heap”
    Update Heap

  • 打开Heap视图
    Heap视图
    Heap Size:应用内存大小
    Allocated:应用已分配内存
    Free:应用空闲内存
    一般占用内存最多的是字节数据“1-byte array”。

  • 点击“Cause GC”,触发内存回收。点击一次就好,之后会自动触发。

  • 应用运行一段时间后,发现内存不断增长,快要达到应用内存上限了,点击“Dump HPROF file”生成HPROF文件,再使用MAT分析该文件。
    生成HPROF文件

    MAT分析内存

    MAT有4个主要视图

  • Overview:应用内存概览
    Overview

  • default_report:描述问题所在,列举占用内存较大的对象
    default_report
  • dominator_tree:列出内存中占用内存的对象
    dominator_tree
    Shallow Heap:对象自身占用的内存
    Retained Heap:对象引用的对象占用的内存
    2个值相差越大,说明有更多的内存没有得到释放。
  • Histogram:以数据类型分组显示占用的内存
    Histogram

byte[]还原为图片

经过分析,发现占用内存最大的是图片,而且有张图片居然占用了8M内存,这就需要把byte[]还原为图片,才能找出是哪张。

  1. 在dominator_tree中,右击对应的byte[],选择List objects-with incoming references
    List objects-with incoming references
  2. 在打开的视图中,右击对应的byte[],选择Copy-Save Value To File,将数据保存为*.data格式。
    Copy-Save Value To File
  3. 选中byte[]对应的Bitmap对象,在Inspector视图中找出mHeight和mWidth值
    Inspector
  4. 使用图片编辑软件GIMP,打开之前保存的data格式文件,在弹出的对话框中,图像类型选择RGB Alpha,高度和宽度填入之前的mHeight和mWidth值
    GIMP

bitmap占用内存计算方式

找出原图后,发现图片占用内存大小和物理大小没有关系,和宽高有直接关系。
计算公式:宽*高*单位像素占用字节数
单位像素占用的字节数由其参数BitmapFactory.Options的inPreferredConfig变量决定,inPreferredConfig为Bitmap.Config类型,默认为Bitmap.Config.ARGB_8888,对应单位像素占用字节数为4。
那张8M的资源图片就是因为宽高太大,导致占用内存太多,对资源图片的宽高要严格限制。

bitmap缓存

项目中使用volley框架,volley已经实现了磁盘缓存,我们只需要实现volley的ImageLoader.ImageCache接口实现内存缓存。
但是,在内存分析中发现BitmapLruImageCache 对象并没有及时的回收内存。所以在代码中增加了达到上限时清除10%缓存的功能。

import java.util.Iterator;
import java.util.Map;

import android.graphics.Bitmap;
import android.os.SystemClock;
import android.support.v4.util.LruCache;
import android.util.Log;

import com.android.volley.toolbox.ImageLoader.ImageCache;

public class BitmapLruImageCache extends LruCache<String, Bitmap> implements
        ImageCache {

    private final String TAG = this.getClass().getSimpleName();

    private static final float HYSTERESIS_FACTOR = 0.9f;

    public BitmapLruImageCache(int maxSize) {
        super(maxSize);
    }

    @Override
    protected int sizeOf(String key, Bitmap value) {
        return value.getRowBytes() * value.getHeight();
    }

    @Override
    public Bitmap getBitmap(String url) {
        Log.v(TAG, "Retrieved item from Mem Cache");
        return get(url);
    }

    @Override
    public void putBitmap(String url, Bitmap bitmap) {
        Log.v(TAG, "Added item to Mem Cache");
        pruneIfNeeded(sizeOf(url, bitmap));
        put(url, bitmap);

            Log.v(TAG,String.format("bitmap Cache size %f MB,maxSize %f MB",
                    this.size()/1024.0/1024.0, this.maxSize()/1024.0/1024.0));
    }

    private void pruneIfNeeded(int neededSpace) {
        if ((this.size() + neededSpace) < this.maxSize()) {
            return;
        }
            Log.v(TAG,"Pruning old cache entries.");

        long before = this.size();
        int prunedFiles = 0;
        long startTime = SystemClock.elapsedRealtime();

        Iterator<Map.Entry<String, Bitmap>> iterator = this.snapshot().entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Bitmap> entry = iterator.next();
            remove(entry.getKey());

            iterator.remove();
            prunedFiles++;

            if ((this.size() + neededSpace) < this.maxSize() * HYSTERESIS_FACTOR) {
                break;
            }
        }

            Log.v(TAG,String.format("pruned %d bitmaps, %d bytes, %d ms",
                    prunedFiles, (this.size() - before), SystemClock.elapsedRealtime() - startTime));
    }
}

fresco

参考网站:http://fresco-cn.org/

Fresco 是一个强大的图片加载组件。

Fresco 中设计有一个叫做 image pipeline 的模块。它负责从网络,从本地文件系统,本地资源加载图片。为了最大限度节省空间和CPU时间,它含有3级缓存设计(2级内存,1级文件)。

Fresco 中设计有一个叫做 Drawees 模块,方便地显示loading图,当图片不再显示在屏幕上时,及时地释放内存和空间占用。

Fresco 支持 Android2.3(API level 9) 及其以上系统。

Fresco 的 Image Pipeline 负责图片的获取和管理。图片可以来自远程服务器,本地文件,或者Content Provider,本地资源。压缩后的文件缓存在本地存储中,Bitmap数据缓存在内存中。

在5.0系统以下,Image Pipeline 使用`pinned purgeables*将Bitmap数据避开Java堆内存,存在ashmem中。这要求图片不使用时,要显式地释放内存。

解压后的图片,即Android中的Bitmap,占用大量的内存。大的内存占用势必引发更加频繁的GC。在5.0以下,GC将会显著地引发界面卡顿。

在5.0以下系统,Fresco将图片放到一个特别的内存区域。当然,在图片不显示的时候,占用的内存会自动被释放。这会使得APP更加流畅,减少因图片内存占用而引发的OOM。

Fresco 在低端机器上表现一样出色,你再也不用因图片内存占用而思前想后。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值