/* Copyright (C) 2008 Christian Schneider
*
* This file is part of Nomad.
*
* Nomad is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Nomad is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Nomad; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.sf.nmedit.nmutils.debug;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Class to test for memory leaks.
*
* Usage:
*
* class TestedClass
* {
*
* private static InstanceCountTest.ClassInstanceCounter ICT =
* InstanceCountTest.getCounter(TestedClass.class);
*
* public TestedClass()
* {
* if (InstanceCountTest.isEnabled()) ICT.newInstance();
* }
*
* protected void finalize() throws Throwable
* {
* if (InstanceCountTest.isEnabled()) ICT.instanceFinalized();
* super.finalize();
* }
*
* }
*/
public class InstanceCountTest
{
private static final boolean ENABLED = true;
public static boolean isEnabled()
{
return ENABLED;
}
public static final class ClassInstanceCounter
{
private Class<?> forClass;
private int count;
private ClassInstanceCounter(Class<?> forClass)
{
this.forClass = forClass;
this.count = 0;
}
public Class<?> getForClass()
{
return forClass;
}
public void newInstance()
{
ensureAutoDumpStarted();
synchronized (forClass)
{
count++;
}
modcounter++;
}
public synchronized void instanceFinalized()
{
synchronized (forClass)
{
count--;
}
modcounter++;
}
public int getCount()
{
synchronized (forClass)
{
return count;
}
}
public String toString()
{
return "Instances for "+forClass+": "+getCount();
}
}
private Map<Class<?>, ClassInstanceCounter> map = Collections.synchronizedMap(new HashMap<Class<?>, ClassInstanceCounter>());
private static InstanceCountTest instance = new InstanceCountTest();
private static boolean autoDumpStarted = false;
private static volatile int modcounter ;
private static void ensureAutoDumpStarted()
{
if (!isEnabled())
return;
if (!autoDumpStarted)
{
synchronized (instance)
{
autoDumpStarted = true;
}
Thread t = new Thread(new AutoDump());
t.setDaemon(true);
t.setName("AutoDump");
t.start();
}
}
private static InstanceCountTest getInstance()
{
return instance;
}
public static ClassInstanceCounter getCounter(Class<?> forClass)
{
InstanceCountTest test = getInstance();
ClassInstanceCounter counter = test.map.get(forClass);
if (counter == null)
{
counter = new ClassInstanceCounter(forClass);
test.map.put(forClass, counter);
modcounter++;
}
return counter;
}
public void printDump()
{
Runtime rt = Runtime.getRuntime();
System.out.println("Instances for (free:"+rt.freeMemory()+" byte, max:"+rt.maxMemory()+")");
for (ClassInstanceCounter counter: map.values())
{
System.out.println("\t"+counter.getForClass()+": "+counter.getCount());
}
}
private static class AutoDump implements Runnable
{
public void run()
{
int lastmod = modcounter-10;
System.out.println("[AutoDump] Started.");
while (!Thread.interrupted())
{
if (instance.map.size()>0 && modcounter != lastmod)
{
instance.printDump();
lastmod = modcounter;
}
try
{
Thread.sleep(1500);
}
catch (InterruptedException e)
{
// no op
}
}
autoDumpStarted = false;
System.out.println("[AutoDump] Stopped.");
}
}
}