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