22
22
*/
23
23
package com .sun .jna ;
24
24
25
+ import java .lang .ref .ReferenceQueue ;
26
+ import java .lang .ref .WeakReference ;
25
27
import java .nio .ByteBuffer ;
26
- import java .util .Collection ;
27
- import java .util .Collections ;
28
- import java .util .LinkedList ;
29
- import java .util .Map ;
30
- import java .util .WeakHashMap ;
28
+ import java .util .ArrayList ;
31
29
32
30
/**
33
31
* A <code>Pointer</code> to memory obtained from the native heap via a
51
49
* @see Pointer
52
50
*/
53
51
public class Memory extends Pointer {
54
- /** Keep track of all allocated memory so we can dispose of it before unloading. */
55
- private static final Map <Memory , Boolean > allocatedMemory =
56
- Collections .synchronizedMap (new WeakHashMap <Memory , Boolean >());
52
+
53
+ private static ReferenceQueue <Memory > QUEUE = new ReferenceQueue <Memory >();
54
+ private static LinkedReference HEAD ; // the head of the doubly linked list used for instance tracking
55
+
56
+ /**
57
+ * Keep track of all allocated memory so we can dispose of it before
58
+ * unloading. This is done using a doubly linked list to enable fast
59
+ * removal of tracked instances.
60
+ */
61
+ private static class LinkedReference extends WeakReference <Memory > {
62
+
63
+ private LinkedReference next ;
64
+ private LinkedReference prev ;
65
+
66
+ private LinkedReference (Memory referent ) {
67
+ super (referent , QUEUE );
68
+ }
69
+
70
+ /**
71
+ * Add the given {@code instance} to the instance tracking.
72
+ *
73
+ * @param instance the instance to track
74
+ */
75
+ static LinkedReference track (Memory instance ) {
76
+ // use a different lock here to allow the finialzier to unlink elements too
77
+ synchronized (QUEUE ) {
78
+ LinkedReference stale ;
79
+
80
+ // handle stale references here to avoid GC overheating when memory is limited
81
+ while ((stale = (LinkedReference ) QUEUE .poll ()) != null ) {
82
+ stale .unlink ();
83
+ }
84
+ }
85
+
86
+ // keep object allocation outside the syncronized block
87
+ LinkedReference entry = new LinkedReference (instance );
88
+
89
+ synchronized (LinkedReference .class ) {
90
+ if (HEAD != null ) {
91
+ entry .next = HEAD ;
92
+ HEAD = HEAD .prev = entry ;
93
+ } else {
94
+ HEAD = entry ;
95
+ }
96
+ }
97
+
98
+ return entry ;
99
+ }
100
+
101
+ /**
102
+ * Remove the related instance from tracking and update the linked list.
103
+ */
104
+ private void unlink () {
105
+ synchronized (LinkedReference .class ) {
106
+ LinkedReference next ;
107
+
108
+ if (HEAD != this ) {
109
+ if (this .prev == null ) {
110
+ // this entry was detached before, e.g. disposeAll was called and finalizers are running now
111
+ return ;
112
+ }
113
+
114
+ next = this .prev .next = this .next ;
115
+ } else {
116
+ next = HEAD = HEAD .next ;
117
+ }
118
+
119
+ if (next != null ) {
120
+ next .prev = this .prev ;
121
+ }
122
+
123
+ // set prev to null to detect detached entries
124
+ this .prev = null ;
125
+ }
126
+ }
127
+ }
57
128
58
129
private static final WeakMemoryHolder buffers = new WeakMemoryHolder ();
59
130
@@ -66,13 +137,71 @@ public static void purge() {
66
137
67
138
/** Dispose of all allocated memory. */
68
139
public static void disposeAll () {
69
- // use a copy since dispose() modifies the map
70
- Collection <Memory > refs = new LinkedList <Memory >(allocatedMemory .keySet ());
71
- for (Memory r : refs ) {
72
- r .dispose ();
140
+ synchronized (LinkedReference .class ) {
141
+ LinkedReference entry ;
142
+
143
+ while ((entry = HEAD ) != null ) {
144
+ Memory memory = HEAD .get ();
145
+
146
+ if (memory != null ) {
147
+ // dispose does the unlink call internal
148
+ memory .dispose ();
149
+ } else {
150
+ HEAD .unlink ();
151
+ }
152
+
153
+ if (HEAD == entry ) {
154
+ throw new IllegalStateException ("the HEAD did not change" );
155
+ }
156
+ }
157
+ }
158
+
159
+ synchronized (QUEUE ) {
160
+ LinkedReference stale ;
161
+
162
+ // try to release as mutch memory as possible
163
+ while ((stale = (LinkedReference ) QUEUE .poll ()) != null ) {
164
+ stale .unlink ();
165
+ }
73
166
}
74
167
}
75
168
169
+ /**
170
+ * Unit-testing only, ensure the doubly linked list is in a good shape.
171
+ *
172
+ * @return the number of tracked instances
173
+ */
174
+ static int integrityCheck () {
175
+ synchronized (LinkedReference .class ) {
176
+ if (HEAD == null ) {
177
+ return 0 ;
178
+ }
179
+
180
+ ArrayList <LinkedReference > entries = new ArrayList <LinkedReference >();
181
+ LinkedReference entry = HEAD ;
182
+
183
+ while (entry != null ) {
184
+ entries .add (entry );
185
+ entry = entry .next ;
186
+ }
187
+
188
+ int index = entries .size () - 1 ;
189
+ entry = entries .get (index );
190
+
191
+ while (entry != null ) {
192
+ if (entries .get (index ) != entry ) {
193
+ throw new IllegalStateException (entries .get (index ) + " vs. " + entry + " at index " + index );
194
+ }
195
+
196
+ entry = entry .prev ;
197
+ index --;
198
+ }
199
+
200
+ return entries .size ();
201
+ }
202
+ }
203
+
204
+ private final LinkedReference reference ; // used to track the instance
76
205
protected long size ; // Size of the malloc'ed space
77
206
78
207
/** Provide a view into the original memory. Keeps an implicit reference
@@ -113,11 +242,13 @@ public Memory(long size) {
113
242
if (peer == 0 )
114
243
throw new OutOfMemoryError ("Cannot allocate " + size + " bytes" );
115
244
116
- allocatedMemory . put (this , Boolean . TRUE );
245
+ reference = LinkedReference . track (this );
117
246
}
118
247
119
248
protected Memory () {
120
249
super ();
250
+
251
+ reference = null ;
121
252
}
122
253
123
254
/** Provide a view of this memory using the given offset as the base address. The
@@ -182,11 +313,18 @@ protected void finalize() {
182
313
183
314
/** Free the native memory and set peer to zero */
184
315
protected synchronized void dispose () {
316
+ if (peer == 0 ) {
317
+ // someone called dispose before, the finalizer will call dispose again
318
+ return ;
319
+ }
320
+
185
321
try {
186
322
free (peer );
187
323
} finally {
188
- allocatedMemory .remove (this );
189
324
peer = 0 ;
325
+ // no null check here, tracking is only null for SharedMemory
326
+ // SharedMemory is overriding the dispose method
327
+ reference .unlink ();
190
328
}
191
329
}
192
330
0 commit comments