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 = 1000;
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 collectableCallCount = 0;
37 private static List<WeakReference<ListenerList<?>>> listenerLists = new LinkedList<WeakReference<ListenerList<?>>>();
38 private static int listenerCallCount = 0;
41 private MemoryManagement() {
46 * Mark an object that should be garbage-collectable by the GC. This class will monitor
47 * whether the object actually gets garbage-collected or not by holding a weak reference
50 * @param o the object to monitor.
52 public static synchronized void collectable(Object o) {
54 throw new IllegalArgumentException("object is null");
56 log.debug("Adding object into collectable list: " + o);
57 objects.add(new MemoryData(o));
58 collectableCallCount++;
59 if (collectableCallCount % PURGE_CALL_COUNT == 0) {
66 * Return a list of MemoryData objects corresponding to the objects that have been
67 * registered by {@link #collectable(Object)} and have not been garbage-collected properly.
68 * This method first calls <code>System.gc()</code> multiple times to attempt to
69 * force any remaining garbage collection.
71 * @return a list of MemoryData objects for objects that have not yet been garbage-collected.
73 public static synchronized List<MemoryData> getRemainingCollectableObjects() {
74 for (int i = 0; i < 5; i++) {
75 System.runFinalization();
79 } catch (InterruptedException e) {
83 return new ArrayList<MemoryData>(objects);
90 * Register a new ListenerList object. This can be used to monitor freeing of listeners
91 * and find memory leaks. The objects are held by a weak reference, allowing them to be
94 * @param list the listener list to register
96 public static synchronized void registerListenerList(ListenerList<?> list) {
97 listenerLists.add(new WeakReference<ListenerList<?>>(list));
99 if (listenerCallCount % PURGE_CALL_COUNT == 0) {
106 * Return a list of listener list objects corresponding to the objects that have been
107 * registered by {@link #registerListenerList(ListenerList)} and have not been garbage-collected yet.
108 * This method first calls <code>System.gc()</code> multiple times to attempt to
109 * force any remaining garbage collection.
111 * @return a list of listener list objects that have not yet been garbage-collected.
113 public static synchronized List<ListenerList<?>> getRemainingListenerLists() {
114 for (int i = 0; i < 5; i++) {
115 System.runFinalization();
119 } catch (InterruptedException e) {
123 List<ListenerList<?>> list = new ArrayList<ListenerList<?>>();
124 for (WeakReference<ListenerList<?>> ref : listenerLists) {
125 ListenerList<?> l = ref.get();
136 * Purge all cleared references from the object list.
138 private static void purgeCollectables() {
139 int origCount = objects.size();
140 Iterator<MemoryData> iterator = objects.iterator();
141 while (iterator.hasNext()) {
142 MemoryData data = iterator.next();
143 if (data.getReference().get() == null) {
147 log.debug(objects.size() + " of " + origCount + " objects remaining in discarded objects list after purge.");
152 * Purge all cleared references from the object list.
154 private static void purgeListeners() {
155 int origCount = listenerLists.size();
156 Iterator<WeakReference<ListenerList<?>>> iterator = listenerLists.iterator();
157 while (iterator.hasNext()) {
158 WeakReference<ListenerList<?>> ref = iterator.next();
159 if (ref.get() == null) {
163 log.debug(listenerLists.size() + " of " + origCount + " listener lists remaining after purge.");
167 * A value object class containing data of a discarded object reference.
169 public static final class MemoryData {
170 private final WeakReference<Object> reference;
171 private final long registrationTime;
173 private MemoryData(Object object) {
174 this.reference = new WeakReference<Object>(object);
175 this.registrationTime = System.currentTimeMillis();
179 * Return the weak reference to the discarded object.
181 public WeakReference<Object> getReference() {
186 * Return the time when the object was discarded.
187 * @return a millisecond timestamp of when the object was discarded.
189 public long getRegistrationTime() {
190 return registrationTime;