1 /* 2 * Copyright (c) 1998, 2008, 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.rmi.transport; 27 28 import java.security.AccessController; 29 import java.security.PrivilegedAction; 30 import java.util.SortedSet; 31 import java.util.TreeSet; 32 33 34 /** 35 * Support for garbage-collection latency requests. 36 * 37 * @author Mark Reinhold 38 * @since 1.2 39 */ 40 41 class GC { 42 43 private GC() { } /* To prevent instantiation */ 44 45 46 /* Latency-target value indicating that there's no active target 47 */ 48 private static final long NO_TARGET = Long.MAX_VALUE; 49 50 /* The current latency target, or NO_TARGET if there is no target 51 */ 52 private static long latencyTarget = NO_TARGET; 53 54 /* The daemon thread that implements the latency-target mechanism, 55 * or null if there is presently no daemon thread 56 */ 57 private static Thread daemon = null; 58 59 /* The lock object for the latencyTarget and daemon fields. The daemon 60 * thread, if it exists, waits on this lock for notification that the 61 * latency target has changed. 62 */ 63 private static class LatencyLock extends Object { }; 64 private static Object lock = new LatencyLock(); 65 66 67 /** 68 * Returns the maximum <em>object-inspection age</em>, which is the number 69 * of real-time milliseconds that have elapsed since the 70 * least-recently-inspected heap object was last inspected by the garbage 71 * collector. 72 * 73 * <p> For simple stop-the-world collectors this value is just the time 74 * since the most recent collection. For generational collectors it is the 75 * time since the oldest generation was most recently collected. Other 76 * collectors are free to return a pessimistic estimate of the elapsed 77 * time, or simply the time since the last full collection was performed. 78 * 79 * <p> Note that in the presence of reference objects, a given object that 80 * is no longer strongly reachable may have to be inspected multiple times 81 * before it can be reclaimed. 82 */ 83 public static native long maxObjectInspectionAge(); 84 85 static { 86 AccessController.doPrivileged(new PrivilegedAction<Void>() { 87 public Void run() { 88 System.loadLibrary("rmi"); 89 return null; 90 }}); 91 } 92 93 private static class Daemon extends Thread { 94 95 public void run() { 96 for (;;) { 97 long l; 98 synchronized (lock) { 99 100 l = latencyTarget; 101 if (l == NO_TARGET) { 102 /* No latency target, so exit */ 103 GC.daemon = null; 104 return; 105 } 106 107 long d = maxObjectInspectionAge(); 108 if (d >= l) { 109 /* Do a full collection. There is a remote possibility 110 * that a full collection will occurr between the time 111 * we sample the inspection age and the time the GC 112 * actually starts, but this is sufficiently unlikely 113 * that it doesn't seem worth the more expensive JVM 114 * interface that would be required. 115 */ 116 System.gc(); 117 d = 0; 118 } 119 120 /* Wait for the latency period to expire, 121 * or for notification that the period has changed 122 */ 123 try { 124 lock.wait(l - d); 125 } catch (InterruptedException x) { 126 continue; 127 } 128 } 129 } 130 } 131 132 private Daemon(ThreadGroup tg) { 133 super(tg, null, "GC Daemon", 0L, false); 134 } 135 136 /* Create a new daemon thread in the root thread group */ 137 public static void create() { 138 PrivilegedAction<Void> pa = new PrivilegedAction<Void>() { 139 public Void run() { 140 ThreadGroup tg = Thread.currentThread().getThreadGroup(); 141 for (ThreadGroup tgn = tg; 142 tgn != null; 143 tg = tgn, tgn = tg.getParent()); 144 Daemon d = new Daemon(tg); 145 d.setDaemon(true); 146 d.setPriority(Thread.MIN_PRIORITY + 1); 147 d.start(); 148 GC.daemon = d; 149 return null; 150 }}; 151 AccessController.doPrivileged(pa); 152 } 153 154 } 155 156 157 /* Sets the latency target to the given value. 158 * Must be invoked while holding the lock. 159 */ 160 private static void setLatencyTarget(long ms) { 161 latencyTarget = ms; 162 if (daemon == null) { 163 /* Create a new daemon thread */ 164 Daemon.create(); 165 } else { 166 /* Notify the existing daemon thread 167 * that the lateency target has changed 168 */ 169 lock.notify(); 170 } 171 } 172 173 174 /** 175 * Represents an active garbage-collection latency request. Instances of 176 * this class are created by the <code>{@link #requestLatency}</code> 177 * method. Given a request, the only interesting operation is that of 178 * cancellation. 179 */ 180 public static class LatencyRequest 181 implements Comparable<LatencyRequest> { 182 183 /* Instance counter, used to generate unique identifers */ 184 private static long counter = 0; 185 186 /* Sorted set of active latency requests */ 187 private static SortedSet<LatencyRequest> requests = null; 188 189 /* Examine the request set and reset the latency target if necessary. 190 * Must be invoked while holding the lock. 191 */ 192 private static void adjustLatencyIfNeeded() { 193 if ((requests == null) || requests.isEmpty()) { 194 if (latencyTarget != NO_TARGET) { 195 setLatencyTarget(NO_TARGET); 196 } 197 } else { 198 LatencyRequest r = requests.first(); 199 if (r.latency != latencyTarget) { 200 setLatencyTarget(r.latency); 201 } 202 } 203 } 204 205 /* The requested latency, or NO_TARGET 206 * if this request has been cancelled 207 */ 208 private long latency; 209 210 /* Unique identifier for this request */ 211 private long id; 212 213 private LatencyRequest(long ms) { 214 if (ms <= 0) { 215 throw new IllegalArgumentException("Non-positive latency: " 216 + ms); 217 } 218 this.latency = ms; 219 synchronized (lock) { 220 this.id = ++counter; 221 if (requests == null) { 222 requests = new TreeSet<LatencyRequest>(); 223 } 224 requests.add(this); 225 adjustLatencyIfNeeded(); 226 } 227 } 228 229 /** 230 * Cancels this latency request. 231 * 232 * @throws IllegalStateException 233 * If this request has already been cancelled 234 */ 235 public void cancel() { 236 synchronized (lock) { 237 if (this.latency == NO_TARGET) { 238 throw new IllegalStateException("Request already" 239 + " cancelled"); 240 } 241 if (!requests.remove(this)) { 242 throw new InternalError("Latency request " 243 + this + " not found"); 244 } 245 if (requests.isEmpty()) requests = null; 246 this.latency = NO_TARGET; 247 adjustLatencyIfNeeded(); 248 } 249 } 250 251 public int compareTo(LatencyRequest r) { 252 long d = this.latency - r.latency; 253 if (d == 0) d = this.id - r.id; 254 return (d < 0) ? -1 : ((d > 0) ? +1 : 0); 255 } 256 257 public String toString() { 258 return (LatencyRequest.class.getName() 259 + "[" + latency + "," + id + "]"); 260 } 261 262 } 263 264 265 /** 266 * Makes a new request for a garbage-collection latency of the given 267 * number of real-time milliseconds. A low-priority daemon thread makes a 268 * best effort to ensure that the maximum object-inspection age never 269 * exceeds the smallest of the currently active requests. 270 * 271 * @param latency 272 * The requested latency 273 * 274 * @throws IllegalArgumentException 275 * If the given <code>latency</code> is non-positive 276 */ 277 public static LatencyRequest requestLatency(long latency) { 278 return new LatencyRequest(latency); 279 } 280 281 282 /** 283 * Returns the current smallest garbage-collection latency request, or zero 284 * if there are no active requests. 285 */ 286 public static long currentLatencyTarget() { 287 long t = latencyTarget; 288 return (t == NO_TARGET) ? 0 : t; 289 } 290 291 }