0

我读了一些关于在 android 中释放内存的内容,但我仍然对这种行为感到困惑。就我而言,我测试了一个简单的应用程序,何时分配内存以及何时释放内存。我有两个活动。在 MainActivity 我有一个 ImageView。getResources().getDrawable(int id)我通过或 via引用可绘制图像BitmapFactory.decodeResource(Resources res, int id)。两种方式都肯定会为图像分配内存,但是即使我破坏了我的活动,回收位图或将所有变量设置为空,也不会释放此内存。

public class MainActivity extends Activity {

private ImageView view;
private Drawable drawable;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    view = (ImageView) findViewById(R.id.image);
    // tried with BitmapFactory.decode...
    drawable = getResources().getDrawable(R.drawable.connect);
    view.setImageDrawable(dr);

    Button button = (Button) findViewById(R.id.button1);
    button.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            Intent intent = new Intent(getApplicationContext(),
                    SecondActivity.class);
            startActivity(intent);
            // tried with and without finish
            finish();
        }
    });
}

@Override
protected void onResume() {
    super.onResume();

    Double allocated = new Double(Debug.getNativeHeapAllocatedSize())
            / new Double((1048576));
    Double available = new Double(Debug.getNativeHeapSize()) / 1048576.0;
    Double free = new Double(Debug.getNativeHeapFreeSize()) / 1048576.0;
    DecimalFormat df = new DecimalFormat();
    df.setMaximumFractionDigits(2);
    df.setMinimumFractionDigits(2);

    System.out.println("SYSO : " + df.format(allocated) + "MB of "
            + df.format(available) + "MB (" + df.format(free) + "MB free)");
    System.out.println("SYSO : "
            + df.format(new Double(
                    Runtime.getRuntime().totalMemory() / 1048576))
            + "MB of "
            + df.format(new Double(
                    Runtime.getRuntime().maxMemory() / 1048576))
            + "MB ("
            + df.format(new Double(
                    Runtime.getRuntime().freeMemory() / 1048576))
            + "MB free)");
}

@Override
protected void onDestroy() {
    super.onDestroy();
    // tried using BitmapFactory and bitmap.recycle()
    dr.setCallback(null);
    dr = null;
    view = null;
    System.gc();
    Runtime.getRuntime().gc();
}
}

我也在我的第二个活动中记录了内存。我发现,我的应用程序在启动时大约有 8-9MB 运行时内存。在主视图中分配我的图像,让内存增长到大约 20MB。当我离开我的活动finish()并使用所有释放的东西(如设置回调 null 和回收图像)时,为什么仍然分配第二个活动中的内存?我多次恢复第二个活动,但内存仍然被分配。我的第一个活动被破坏了,我该如何释放它的内存?我在没有设置callback = null或回收位图并完成MainActivity. 然后每次我恢复MainActivity,每个简历的内存增长约 10MB。听起来不错,因为旧的引用不会被破坏,并且每次都会分配一个新的图像。但是为什么第一张图片的初始内存不会被破坏呢?

4

2 回答 2

1

MAT中的直方图

在这里,您可以看到 MAT 的直方图视图。我完成了 MainActivity 并启动了 SecondActivity。但是 FinalizerReference 仍然持有 MainActivity。为什么?

于 2013-07-29T07:08:21.910 回答
1

我认为你有内存泄漏,首先在 google I/O 2011 上观看这个关于内存管理的非常好的视频 -> http://www.youtube.com/watch?v=_CruQY55HOk然后在了解垃圾收集之后,你可以尝试使用 eclipse MAT (Memory Analyzer Tool) 查找内存泄漏http://www.eclipse.org/mat/

对于位图,我建议您重用位图以将其缓存在内存中。您可以像这样实现它:

public class MemoryCache {

    String TAG = "MemoryCache";
    private Map<String, Bitmap> cache=Collections.synchronizedMap(
            new LinkedHashMap<String, Bitmap>(10,1.5f,true));//Last argument true for LRU ordering
    private long size=0;//current allocated size
    private long limit=1000000;//max memory in bytes

    public MemoryCache(){
        //use 25% of available heap size
        setLimit(Runtime.getRuntime().maxMemory()/4);
    }

    public void setLimit(long new_limit){
        limit=new_limit;
    }

    public Bitmap get(String id){
        try{
            if(!cache.containsKey(id))
                return null;
            //NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78 
            return cache.get(id);
        }catch(NullPointerException ex){
            return null;
        }
    }

    public void put(String id, Bitmap bitmap){
        try{
            if(cache.containsKey(id))
                size-=getSizeInBytes(cache.get(id));
            cache.put(id, bitmap);
            size+=getSizeInBytes(bitmap);
            checkSize();
        }catch(Throwable th){
            th.printStackTrace();
        }
    }

    private void checkSize() {
        if(size>limit){
            Iterator<Entry<String, Bitmap>> iter=cache.entrySet().iterator();//least recently accessed item will be the first one iterated  
            while(iter.hasNext()){
                Entry<String, Bitmap> entry=iter.next();
                size-=getSizeInBytes(entry.getValue());
                iter.remove();
                if(size<=limit)
                    break;
            }
        }
    }

    public void clear() {
        cache.clear();
    }

    long getSizeInBytes(Bitmap bitmap) {
        if(bitmap==null)
            return 0;
        return bitmap.getRowBytes() * bitmap.getHeight();
    }
}

并像这样存储它:

memoryCache.put(photoToLoad.url, bmp);

并在创建新位图之前检查您的 url 或 URI 或可绘制名称是否存在于缓存中

WeakReference<Bitmap> bitmap = new WeakReference<Bitmap>(
                memoryCache.get(url));

我假设您知道如何修改我的代码以存储来自可绘制或 URI 的缓存,但是如果您对我的回答有疑问,请随时在评论中提问,如果您觉得我的回答有用,请不要忘记投票并接受所以其他人也可以读到这个:)

于 2013-07-26T07:54:01.413 回答