1 /* 2 * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.java2d; 27 28 import java.lang.ref.Reference; 29 import java.lang.ref.ReferenceQueue; 30 import java.lang.ref.PhantomReference; 31 import java.lang.ref.WeakReference; 32 import java.util.ArrayList; 33 import java.util.Hashtable; 34 35 /** 36 * This class is used for registering and disposing the native 37 * data associated with java objects. 38 * 39 * The object can register itself by calling one of the addRecord 40 * methods and providing either the pointer to the native disposal 41 * method or a descendant of the DisposerRecord class with overridden 42 * dispose() method. 43 * 44 * When the object becomes unreachable, the dispose() method 45 * of the associated DisposerRecord object will be called. 46 * 47 * @see DisposerRecord 48 */ 49 public class Disposer implements Runnable { 50 private static final ReferenceQueue queue = new ReferenceQueue(); 51 private static final Hashtable records = new Hashtable(); 52 53 private static Disposer disposerInstance; 54 public static final int WEAK = 0; 55 public static final int PHANTOM = 1; 56 public static int refType = PHANTOM; 57 58 static { 59 java.security.AccessController.doPrivileged( 60 new sun.security.action.LoadLibraryAction("awt")); 61 initIDs(); 62 String type = (String) java.security.AccessController.doPrivileged( 63 new sun.security.action.GetPropertyAction("sun.java2d.reftype")); 64 if (type != null) { 65 if (type.equals("weak")) { 66 refType = WEAK; 67 System.err.println("Using WEAK refs"); 68 } else { 69 refType = PHANTOM; 70 System.err.println("Using PHANTOM refs"); 71 } 72 } 73 disposerInstance = new Disposer(); 74 java.security.AccessController.doPrivileged( 75 new java.security.PrivilegedAction() { 76 public Object run() { 77 /* The thread must be a member of a thread group 78 * which will not get GCed before VM exit. 79 * Make its parent the top-level thread group. 80 */ 81 ThreadGroup tg = Thread.currentThread().getThreadGroup(); 82 for (ThreadGroup tgn = tg; 83 tgn != null; 84 tg = tgn, tgn = tg.getParent()); 85 Thread t = 86 new Thread(tg, disposerInstance, "Java2D Disposer"); 87 t.setContextClassLoader(null); 88 t.setDaemon(true); 89 t.setPriority(Thread.MAX_PRIORITY); 90 t.start(); 91 return null; 92 } 93 } 94 ); 95 } 96 97 /** 98 * Registers the object and the native data for later disposal. 99 * @param target Object to be registered 100 * @param disposeMethod pointer to the native disposal method 101 * @param pData pointer to the data to be passed to the 102 * native disposal method 103 */ 104 public static void addRecord(Object target, 105 long disposeMethod, long pData) 106 { 107 disposerInstance.add(target, 108 new DefaultDisposerRecord(disposeMethod, pData)); 109 } 110 111 /** 112 * Registers the object and the native data for later disposal. 113 * @param target Object to be registered 114 * @param rec the associated DisposerRecord object 115 * @see DisposerRecord 116 */ 117 public static void addRecord(Object target, DisposerRecord rec) { 118 disposerInstance.add(target, rec); 119 } 120 121 /** 122 * Performs the actual registration of the target object to be disposed. 123 * @param target Object to be registered, or if target is an instance 124 * of DisposerTarget, its associated disposer referent 125 * will be the Object that is registered 126 * @param rec the associated DisposerRecord object 127 * @see DisposerRecord 128 */ 129 synchronized void add(Object target, DisposerRecord rec) { 130 if (target instanceof DisposerTarget) { 131 target = ((DisposerTarget)target).getDisposerReferent(); 132 } 133 java.lang.ref.Reference ref; 134 if (refType == PHANTOM) { 135 ref = new PhantomReference(target, queue); 136 } else { 137 ref = new WeakReference(target, queue); 138 } 139 records.put(ref, rec); 140 } 141 142 public void run() { 143 while (true) { 144 try { 145 Object obj = queue.remove(); 146 ((Reference)obj).clear(); 147 DisposerRecord rec = (DisposerRecord)records.remove(obj); 148 rec.dispose(); 149 obj = null; 150 rec = null; 151 clearDeferredRecords(); 152 } catch (Exception e) { 153 System.out.println("Exception while removing reference: " + e); 154 e.printStackTrace(); 155 } 156 } 157 } 158 159 /* 160 * This is a marker interface that, if implemented, means it 161 * doesn't acquire any special locks, and is safe to 162 * be disposed in the poll loop on whatever thread 163 * which happens to be the Toolkit thread, is in use. 164 */ 165 public static interface PollDisposable { 166 }; 167 168 private static ArrayList<DisposerRecord> deferredRecords = null; 169 170 private static void clearDeferredRecords() { 171 if (deferredRecords == null || deferredRecords.isEmpty()) { 172 return; 173 } 174 for (int i=0;i<deferredRecords.size(); i++) { 175 try { 176 DisposerRecord rec = deferredRecords.get(i); 177 rec.dispose(); 178 } catch (Exception e) { 179 System.out.println("Exception while disposing deferred rec."); 180 e.printStackTrace(); 181 } 182 } 183 deferredRecords.clear(); 184 } 185 186 /* 187 * Set to indicate the queue is presently being polled. 188 */ 189 public static volatile boolean pollingQueue = false; 190 191 /* 192 * The pollRemove() method is called back from a dispose method 193 * that is running on the toolkit thread and wants to 194 * dispose any pending refs that are safe to be disposed 195 * on that thread. 196 */ 197 public static void pollRemove() { 198 199 /* This should never be called recursively, so this check 200 * is just a safeguard against the unexpected. 201 */ 202 if (pollingQueue) { 203 return; 204 } 205 Object obj; 206 pollingQueue = true; 207 int freed = 0; 208 int deferred = 0; 209 try { 210 while ((obj = queue.poll()) != null 211 && freed < 10000 && deferred < 100) { 212 freed++; 213 ((Reference)obj).clear(); 214 DisposerRecord rec = (DisposerRecord)records.remove(obj); 215 if (rec instanceof PollDisposable) { 216 rec.dispose(); 217 obj = null; 218 rec = null; 219 } else { 220 if (rec == null) { // shouldn't happen, but just in case. 221 continue; 222 } 223 deferred++; 224 if (deferredRecords == null) { 225 deferredRecords = new ArrayList<DisposerRecord>(5); 226 } 227 deferredRecords.add(rec); 228 } 229 } 230 } catch (Exception e) { 231 System.out.println("Exception while removing reference: " + e); 232 e.printStackTrace(); 233 } finally { 234 pollingQueue = false; 235 } 236 } 237 238 private static native void initIDs(); 239 240 /* 241 * This was added for use by the 2D font implementation to avoid creation 242 * of an additional disposer thread. 243 * WARNING: this thread class monitors a specific queue, so a reference 244 * added here must have been created with this queue. Failure to do 245 * so will clutter the records hashmap and no one will be cleaning up 246 * the reference queue. 247 */ 248 public static void addReference(Reference ref, DisposerRecord rec) { 249 records.put(ref, rec); 250 } 251 252 public static void addObjectRecord(Object obj, DisposerRecord rec) { 253 records.put(new WeakReference(obj, queue) , rec); 254 } 255 256 /* This is intended for use in conjunction with addReference(..) 257 */ 258 public static ReferenceQueue getQueue() { 259 return queue; 260 } 261 262 }