/*
* @(#)VM.java 1.21 06/10/10
*
* Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*
*/
package sun.misc;
import java.util.Vector;
/**
* The Sun.misc.vm class provides an interface to the memory
* management system of the Virtual Machine so that applications
* and applets can be informed of the state of memory system.
* Memory States :
* <li>Green
* When the memory state is "Green", the vm has sufficient memory
* to run normally.
* <li>Yellow
* When the memory state is "Yellow", the vm still has sufficient
* memory to run, but large allocations may fail, so an application
* may wish to free unnecessary resources or make other space saving trade-offs.
* <li>Red
* When the memory state is Red, the vm is critically low on memory.
* All unnecessary memory should be freed and unnecessary applications should exit.
* <p>
* These states are represented with the constants
* <code>VM.STATE_GREEN, VM.STATE_YELLOW, and VM.STATE_RED.</code>
* <h3>Compatibility</h3>
* Some PersonalJava implementations support the Sun.misc.vm class.
* It is not supported in the J2SE.
*/
public class VM implements Runnable {
public static final int STATE_GREEN = 1;
public static final int STATE_YELLOW = 2;
public static final int STATE_RED = 3;
private byte memoryAdvice = STATE_GREEN; // Updated by GC
private Object memoryAdviceLock = new Object();
private Vector callbacks = new Vector(1);
private Object[] callbacksCopy = new Object[1];
private Thread callbackThread;
private static VM singleton = null;
/**
* Compute the current memory state. This may be an expensive operation,
* because a GC might be triggered to produce an accurate result.
* @return the current memory state
*
*/
// public static final native int getState();
/**
* Report the (approximate) memory state at the end of the last GC. This will
* be a quick operation that should give a useful approximation of the
* real state of the system.
*
* @return the current memory state
*/
public static final int getStateQuick() {
return getSingleton().memoryAdvice;
}
/**
* Register a notifier to be informed when availability of
* memory in the system changes state.
* <p>
* After calling the <code>registerVMNotification()</code> method, the
* notifier will be invoked when the specified changes in memory state occur.
* @see sun.misc.VMNotification
*/
public static void registerVMNotification(VMNotification n) {
getSingleton().registerCallback(n);
}
//
// Don't let anyone else instantiate this class
//
private VM() {// registerWithGC();
}
private static VM getSingleton() {
if (singleton == null) {
createSingleton();
}
return singleton;
}
private static synchronized void createSingleton() {
if (singleton == null) { // We must re-test this within synchronized
singleton = new VM();
}
}
// private native void registerWithGC();
private void registerCallback(VMNotification n) {
synchronized (this) {
if (callbackThread == null) {
callbackThread = new Thread(singleton, "RYG Callback Thread");
callbackThread.setPriority(Thread.MAX_PRIORITY - 1);
callbackThread.start();
}
// we update callbacksCopy first in case the memory allocation
// triggers a state change. The gc will attempt to acquire
// the lock that we already own, which is ok since it's
// only going to update fields we're not playing with.
synchronized (callbacks) {
if (callbacks.size() + 1 > callbacksCopy.length) {
callbacksCopy = new Object[callbacks.size() + 1];
}
callbacks.addElement(n);
}
}
}
/**
* User code is not meant to call this.
*/
// It's public because that's a requirement of runnable. This isn't
// a security hole, because untrusted code can't call into sun.*.
public void run() {
int previousState = STATE_GREEN;
int currentState;
while (true) {
try {
while (true) {
synchronized (memoryAdviceLock) {
currentState = memoryAdvice;
if (currentState != previousState) {
break;
}
memoryAdviceLock.wait();
}
}
// We go to some effort to avoid doing callbacks while holding
// locks. Doing otherwise is just asking for trouble.
int copySize;
Object[] cbc;
synchronized (callbacks) {
// guaranteed to fit by registerCallback
callbacks.copyInto(callbacksCopy);
copySize = callbacks.size();
// callbacksCopy (the variable, not the object)
// may get stored into from registerCallback, so
// make a copy of the reference.
cbc = callbacksCopy;
}
// do the callbacks outside of the synchronized block to
// avoid locking problems
for (int i = 0; i < copySize; i++) {
VMNotification n = (VMNotification) cbc[i];
try {
n.newAllocState(previousState, currentState, true);
} catch (Throwable t) {
try {
t.printStackTrace();
} catch (Throwable ignored) {}
}
}
previousState = currentState;
} catch (Throwable t) {
try {
// don't let the RYG Callback thread exit for any reason.
t.printStackTrace();
} catch (Throwable e) {}
}
}
}
}