我的一类解决方案:“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;
}
}
}
}