create changelog entry
[debian/openrocket] / core / src / net / sf / openrocket / util / MemoryManagement.java
1 package net.sf.openrocket.util;
2
3 import java.lang.ref.WeakReference;
4 import java.util.ArrayList;
5 import java.util.Iterator;
6 import java.util.LinkedList;
7 import java.util.List;
8
9 import net.sf.openrocket.logging.LogHelper;
10 import net.sf.openrocket.startup.Application;
11
12 /**
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.
19  * 
20  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
21  */
22 public final class MemoryManagement {
23         private static final LogHelper log = Application.getLogger();
24         
25         /** Purge cleared references every this many calls to {@link #collectable(Object)} */
26         private static final int PURGE_CALL_COUNT = 1000;
27         
28
29         /**
30          * Storage of the objects.  This is basically a mapping from the objects (using weak references)
31          * to 
32          */
33         private static List<MemoryData> objects = new LinkedList<MemoryData>();
34         private static int collectableCallCount = 0;
35         
36
37         private static List<WeakReference<ListenerList<?>>> listenerLists = new LinkedList<WeakReference<ListenerList<?>>>();
38         private static int listenerCallCount = 0;
39         
40         
41         private MemoryManagement() {
42         }
43         
44         
45         /**
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
48          * to the object.
49          * 
50          * @param o             the object to monitor.
51          */
52         public static synchronized void collectable(Object o) {
53                 if (o == null) {
54                         throw new IllegalArgumentException("object is null");
55                 }
56                 log.debug("Adding object into collectable list: " + o);
57                 objects.add(new MemoryData(o));
58                 collectableCallCount++;
59                 if (collectableCallCount % PURGE_CALL_COUNT == 0) {
60                         purgeCollectables();
61                 }
62         }
63         
64         
65         /**
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.
70          * 
71          * @return      a list of MemoryData objects for objects that have not yet been garbage-collected.
72          */
73         public static synchronized List<MemoryData> getRemainingCollectableObjects() {
74                 for (int i = 0; i < 5; i++) {
75                         System.runFinalization();
76                         System.gc();
77                         try {
78                                 Thread.sleep(1);
79                         } catch (InterruptedException e) {
80                         }
81                 }
82                 purgeCollectables();
83                 return new ArrayList<MemoryData>(objects);
84         }
85         
86         
87
88
89         /**
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
92          * garbage-collected.
93          * 
94          * @param list  the listener list to register
95          */
96         public static synchronized void registerListenerList(ListenerList<?> list) {
97                 listenerLists.add(new WeakReference<ListenerList<?>>(list));
98                 listenerCallCount++;
99                 if (listenerCallCount % PURGE_CALL_COUNT == 0) {
100                         purgeListeners();
101                 }
102                 
103         }
104         
105         /**
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.
110          * 
111          * @return      a list of listener list objects that have not yet been garbage-collected.
112          */
113         public static synchronized List<ListenerList<?>> getRemainingListenerLists() {
114                 for (int i = 0; i < 5; i++) {
115                         System.runFinalization();
116                         System.gc();
117                         try {
118                                 Thread.sleep(1);
119                         } catch (InterruptedException e) {
120                         }
121                 }
122                 purgeListeners();
123                 List<ListenerList<?>> list = new ArrayList<ListenerList<?>>();
124                 for (WeakReference<ListenerList<?>> ref : listenerLists) {
125                         ListenerList<?> l = ref.get();
126                         if (l != null) {
127                                 list.add(l);
128                         }
129                 }
130                 return list;
131         }
132         
133         
134
135         /**
136          * Purge all cleared references from the object list.
137          */
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) {
144                                 iterator.remove();
145                         }
146                 }
147                 log.debug(objects.size() + " of " + origCount + " objects remaining in discarded objects list after purge.");
148         }
149         
150         
151         /**
152          * Purge all cleared references from the object list.
153          */
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) {
160                                 iterator.remove();
161                         }
162                 }
163                 log.debug(listenerLists.size() + " of " + origCount + " listener lists remaining after purge.");
164         }
165         
166         /**
167          * A value object class containing data of a discarded object reference.
168          */
169         public static final class MemoryData {
170                 private final WeakReference<Object> reference;
171                 private final long registrationTime;
172                 
173                 private MemoryData(Object object) {
174                         this.reference = new WeakReference<Object>(object);
175                         this.registrationTime = System.currentTimeMillis();
176                 }
177                 
178                 /**
179                  * Return the weak reference to the discarded object.
180                  */
181                 public WeakReference<Object> getReference() {
182                         return reference;
183                 }
184                 
185                 /**
186                  * Return the time when the object was discarded.
187                  * @return      a millisecond timestamp of when the object was discarded.
188                  */
189                 public long getRegistrationTime() {
190                         return registrationTime;
191                 }
192         }
193         
194 }