1 /* 2 * Copyright (c) 2002, 2014, 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 sun.awt.util.ThreadGroupUtils; 29 30 import java.lang.ref.Reference; 31 import java.lang.ref.ReferenceQueue; 32 import java.lang.ref.PhantomReference; 33 import java.lang.ref.WeakReference; 34 import java.security.AccessController; 35 import java.security.PrivilegedAction; 36 import java.util.ArrayList; 37 import java.util.Hashtable; 38 39 /** 40 * This class is used for registering and disposing the native 41 * data associated with java objects. 42 * 43 * The object can register itself by calling one of the addRecord 44 * methods and providing either the pointer to the native disposal 45 * method or a descendant of the DisposerRecord class with overridden 46 * dispose() method. 47 * 48 * When the object becomes unreachable, the dispose() method 49 * of the associated DisposerRecord object will be called. 50 * 51 * @see DisposerRecord 52 */ 53 public class Disposer implements Runnable { 54 private static final ReferenceQueue<Object> queue = new ReferenceQueue<>(); 55 private static final Hashtable<java.lang.ref.Reference<Object>, DisposerRecord> records = 56 new Hashtable<>(); 57 58 private static Disposer disposerInstance; 59 public static final int WEAK = 0; 60 public static final int PHANTOM = 1; 61 public static int refType = PHANTOM; 62 63 static { 64 java.security.AccessController.doPrivileged( 65 new java.security.PrivilegedAction<Void>() { 66 public Void run() { 67 System.loadLibrary("awt"); 68 return null; 69 } 70 }); 71 initIDs(); 72 String type = java.security.AccessController.doPrivileged( 73 new sun.security.action.GetPropertyAction("sun.java2d.reftype")); 74 if (type != null) { 75 if (type.equals("weak")) { 76 refType = WEAK; 77 System.err.println("Using WEAK refs"); 78 } else { 79 refType = PHANTOM; 80 System.err.println("Using PHANTOM refs"); 81 } 82 } 83 disposerInstance = new Disposer(); 84 AccessController.doPrivileged( 85 (PrivilegedAction<Void>) () -> { 86 /* The thread must be a member of a thread group 87 * which will not get GCed before VM exit. 88 * Make its parent the top-level thread group. 89 */ 90 ThreadGroup rootTG = ThreadGroupUtils.getRootThreadGroup(); 91 Thread t = new Thread(rootTG, disposerInstance, "Java2D Disposer"); 92 t.setContextClassLoader(null); 93 t.setDaemon(true); 94 t.setPriority(Thread.MAX_PRIORITY); 95 t.start(); 96 return null; 97 } 98 ); 99 } 100 101 /** 102 * Registers the object and the native data for later disposal. 103 * @param target Object to be registered 104 * @param disposeMethod pointer to the native disposal method 105 * @param pData pointer to the data to be passed to the 106 * native disposal method 107 */ 108 public static void addRecord(Object target, 109 long disposeMethod, long pData) 110 { 111 disposerInstance.add(target, 112 new DefaultDisposerRecord(disposeMethod, pData)); 113 } 114 115 /** 116 * Registers the object and the native data for later disposal. 117 * @param target Object to be registered 118 * @param rec the associated DisposerRecord object 119 * @see DisposerRecord 120 */ 121 public static void addRecord(Object target, DisposerRecord rec) { 122 disposerInstance.add(target, rec); 123 } 124 125 /** 126 * Performs the actual registration of the target object to be disposed. 127 * @param target Object to be registered, or if target is an instance 128 * of DisposerTarget, its associated disposer referent 129 * will be the Object that is registered 130 * @param rec the associated DisposerRecord object 131 * @see DisposerRecord 132 */ 133 synchronized void add(Object target, DisposerRecord rec) { 134 if (target instanceof DisposerTarget) { 135 target = ((DisposerTarget)target).getDisposerReferent(); 136 } 137 java.lang.ref.Reference<Object> ref; 138 if (refType == PHANTOM) { 139 ref = new PhantomReference<>(target, queue); 140 } else { 141 ref = new WeakReference<>(target, queue); 142 } 143 records.put(ref, rec); 144 } 145 146 public void run() { 147 while (true) { 148 try { 149 Object obj = queue.remove(); 150 ((Reference)obj).clear(); 151 DisposerRecord rec = records.remove(obj); 152 rec.dispose(); 153 obj = null; 154 rec = null; 155 clearDeferredRecords(); 156 } catch (Exception e) { 157 System.out.println("Exception while removing reference."); 158 } 159 } 160 } 161 162 /* 163 * This is a marker interface that, if implemented, means it 164 * doesn't acquire any special locks, and is safe to 165 * be disposed in the poll loop on whatever thread 166 * which happens to be the Toolkit thread, is in use. 167 */ 168 public static interface PollDisposable { 169 }; 170 171 private static ArrayList<DisposerRecord> deferredRecords = null; 172 173 private static void clearDeferredRecords() { 174 if (deferredRecords == null || deferredRecords.isEmpty()) { 175 return; 176 } 177 for (int i=0;i<deferredRecords.size(); i++) { 178 try { 179 DisposerRecord rec = deferredRecords.get(i); 180 rec.dispose(); 181 } catch (Exception e) { 182 System.out.println("Exception while disposing deferred rec."); 183 } 184 } 185 deferredRecords.clear(); 186 } 187 188 /* 189 * Set to indicate the queue is presently being polled. 190 */ 191 public static volatile boolean pollingQueue = false; 192 193 /* 194 * The pollRemove() method is called back from a dispose method 195 * that is running on the toolkit thread and wants to 196 * dispose any pending refs that are safe to be disposed 197 * on that thread. 198 */ 199 public static void pollRemove() { 200 201 /* This should never be called recursively, so this check 202 * is just a safeguard against the unexpected. 203 */ 204 if (pollingQueue) { 205 return; 206 } 207 Object obj; 208 pollingQueue = true; 209 int freed = 0; 210 int deferred = 0; 211 try { 212 while ((obj = queue.poll()) != null 213 && freed < 10000 && deferred < 100) { 214 freed++; 215 ((Reference)obj).clear(); 216 DisposerRecord rec = records.remove(obj); 217 if (rec instanceof PollDisposable) { 218 rec.dispose(); 219 obj = null; 220 rec = null; 221 } else { 222 if (rec == null) { // shouldn't happen, but just in case. 223 continue; 224 } 225 deferred++; 226 if (deferredRecords == null) { 227 deferredRecords = new ArrayList<DisposerRecord>(5); 228 } 229 deferredRecords.add(rec); 230 } 231 } 232 } catch (Exception e) { 233 System.out.println("Exception while removing reference."); 234 } finally { 235 pollingQueue = false; 236 } 237 } 238 239 private static native void initIDs(); 240 241 /* 242 * This was added for use by the 2D font implementation to avoid creation 243 * of an additional disposer thread. 244 * WARNING: this thread class monitors a specific queue, so a reference 245 * added here must have been created with this queue. Failure to do 246 * so will clutter the records hashmap and no one will be cleaning up 247 * the reference queue. 248 */ 249 @SuppressWarnings("unchecked") 250 public static void addReference(Reference<Object> ref, DisposerRecord rec) { 251 records.put(ref, rec); 252 } 253 254 public static void addObjectRecord(Object obj, DisposerRecord rec) { 255 records.put(new WeakReference<>(obj, queue) , rec); 256 } 257 258 /* This is intended for use in conjunction with addReference(..) 259 */ 260 public static ReferenceQueue<Object> getQueue() { 261 return queue; 262 } 263 264 }