0

在重建了我曾经写过的一段旧代码之后,然后忘记了,现在重写了……我把它作为一个 wiki 放在这里,供所有人使用:-)

所以,基本上:如果你在一个复杂的 Android 应用程序中出现内存泄漏,包含图像和交叉引用。您将如何去查找哪些(类型)对象正在泄漏?Android SDK 提供了一些(很难学习和使用)工具。可能还有更多我不知道的。然而,Java 确实提供了 PhantomReference 作为执行此操作的一种手段,即使经历设置所需类所需的混乱可能需要很多工作(而且也很讨厌...... JDK-8034946)。

但是最简单/最有效的方法是什么?我的解决方案如下。

4

2 回答 2

1

LeakCanary是一个自动检测内存泄漏的第三方库,在添加依赖项后,您可以将以下行添加到您的应用程序类中:

LeakCanary.install(this);

该库提供了一个很好的泄漏通知和跟踪,您还可以定义自己的参考观察者(尽管默认的观察者似乎工作得很好)。

于 2015-10-15T13:07:01.370 回答
0

我的一类解决方案:“MemCheck”

要监视任何对象,只需调用:

MemCheck.add( this ); // in any class constructor

这将“监控”分配的对象数量,最重要的是 - 已释放。

要在任何需要的时间记录泄漏,请致电:

MemCheck.countAndLog();

作为替代方案,设置 MemCheck.periodic = 5(秒数)。这将每 5 秒报告一次内存中被监控对象的数量。它还将方便地记录使用/空闲内存。

所以,MemCheck.java:

package com.xyz.util;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.TreeMap;
import android.os.Handler;
import android.util.Log;

public class MemCheck
{
    private static final boolean                enabled = true;
    private static final int                    periodic = 0; // seconds, 0 == disabled
    private static final String                 tag = MemCheck.class.getName();
    private static TreeMap<String, RefCount>    mObjectMap = new TreeMap<String, RefCount>(); 
    private static Runnable                     mPeriodicRunnable = null;
    private static Handler                      mPeriodicHandler = null;

    public static void add( Object object )
    {
        if( !enabled )
            return;

        synchronized( mObjectMap )
        {
            String name = object.getClass().getName();
            RefCount queue = mObjectMap.get( name );
            if( queue == null )
            {
                queue = new RefCount();
                mObjectMap.put( name, queue );
                queue.add( object );
            }
            else
                queue.add( object );
        }
    }

    public static void countAndLog()
    {
        if( !enabled )
            return;
        System.gc();
        Log.d( tag, "Log report starts" );
        Iterator<Entry<String, RefCount>> entryIter = mObjectMap.entrySet().iterator();
        while( entryIter.hasNext() )
        {
            Entry<String, RefCount> entry = entryIter.next(); 
            String name = entry.getKey();
            RefCount refCount = entry.getValue();
            Log.d( tag, "Class " + name + " has " + refCount.countRefs() + " objects in memory." );
        }

        logMemoryUsage();
        Log.d( tag, "Log report done" );
    }

    public static void logMemoryUsage()
    {
        if( !enabled )
            return;
        Runtime runtime = Runtime.getRuntime();
        Log.d( tag, "Max Heap: " + runtime.maxMemory() / 1048576 + " MB, Used: " + runtime.totalMemory() / 1048576 +
               " MB, Free: " + runtime.freeMemory() / 1024 + " MB" );

        if( periodic > 0 )
        {
            if( mPeriodicRunnable != null )
                mPeriodicHandler.removeCallbacks( mPeriodicRunnable );
            if( mPeriodicHandler == null )
                mPeriodicHandler = new Handler();
            mPeriodicRunnable = new Runnable()
            {
                @Override
                public void run()
                {
                    mPeriodicRunnable = null;
                    countAndLog();
                    logMemoryUsage(); // this will run the next
                }
            };

            mPeriodicHandler.postDelayed( mPeriodicRunnable, periodic * 1000 );
        }
    }

    private static class RefCount
    {
        private ReferenceQueue<Object>  mQueue = new ReferenceQueue<Object>();
        private HashSet<Object>         mRefHash = new HashSet<Object>(); 
        private int                     mRefCount = 0;

        public void add( Object o )
        {
            synchronized( this )
            {
                mRefHash.add( new PhantomReference<Object>( o, mQueue ) ); // Note: References MUST be kept alive for their references to be enqueued
                mRefCount++;
            }
        }

        public int countRefs()
        {
            synchronized( this )
            {
                Object ref;
                while( ( ref = mQueue.poll() ) != null )
                {
                    mRefHash.remove( ref );
                    mRefCount--;
                }

                return mRefCount;
            }
        }

    }
}
于 2015-10-15T12:53:50.083 回答