1 package net.sf.openrocket.util;
3 import java.lang.ref.WeakReference;
4 import java.util.ArrayList;
5 import java.util.Iterator;
6 import java.util.LinkedList;
9 import net.sf.openrocket.logging.LogHelper;
10 import net.sf.openrocket.startup.Application;
13 * A class that performs certain memory-management operations for debugging purposes.
14 * For example, complex objects that are being discarded and that should be garbage-collectable
15 * (such as dialog windows) should be registered for monitoring by calling
16 * {@link #collectable(Object)}. This will allow monitoring whether the object really is
17 * garbage-collected or whether it is retained in memory due to a memory leak.
18 * Only complex objects should be registered due to the overhead of the monitoring.
20 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
22 public final class MemoryManagement {
23 private static final LogHelper log = Application.getLogger();
25 /** Purge cleared references every this many calls to {@link #collectable(Object)} */
26 private static final int PURGE_CALL_COUNT = 100;
30 * Storage of the objects. This is basically a mapping from the objects (using weak references)
33 private static List<MemoryData> objects = new LinkedList<MemoryData>();
34 private static int callCount = 0;
37 private MemoryManagement() {
42 * Mark an object that should be garbage-collectable by the GC. This class will monitor
43 * whether the object actually gets garbage-collected or not by holding a weak reference
46 * @param o the object to monitor.
48 public static synchronized void collectable(Object o) {
50 throw new IllegalArgumentException("object is null");
52 log.debug("Adding object into collectable list: " + o);
53 objects.add(new MemoryData(o));
55 if (callCount % PURGE_CALL_COUNT == 0) {
62 * Return the number of times {@link #collectable(Object)} has been called.
63 * @return the number of times {@link #collectable(Object)} has been called.
65 public static synchronized int getCallCount() {
71 * Return a list of MemoryData objects corresponding to the objects that have been
72 * registered by {@link #collectable(Object)} and have not been garbage-collected properly.
73 * This method first calls <code>System.gc()</code> multiple times to attempt to
74 * force any remaining garbage collection.
76 * @return a list of MemoryData objects for objects that have not yet been garbage-collected.
78 public static synchronized ArrayList<MemoryData> getRemainingObjects() {
79 for (int i = 0; i < 5; i++) {
80 System.runFinalization();
84 } catch (InterruptedException e) {
88 return new ArrayList<MemoryData>(objects);
94 * Purge all cleared references from the object list.
96 private static void purge() {
97 int origCount = objects.size();
98 Iterator<MemoryData> iterator = objects.iterator();
99 while (iterator.hasNext()) {
100 MemoryData data = iterator.next();
101 if (data.getReference().get() == null) {
105 log.debug(objects.size() + " of " + origCount + " objects remaining in discarded objects list after purge.");
110 * A value object class containing data of a discarded object reference.
112 public static final class MemoryData {
113 private final WeakReference<Object> reference;
114 private final long registrationTime;
116 private MemoryData(Object object) {
117 this.reference = new WeakReference<Object>(object);
118 this.registrationTime = System.currentTimeMillis();
122 * Return the weak reference to the discarded object.
124 public WeakReference<Object> getReference() {
129 * Return the time when the object was discarded.
130 * @return a millisecond timestamp of when the object was discarded.
132 public long getRegistrationTime() {
133 return registrationTime;