src/share/classes/java/util/logging/LogManager.java
Print this page
@@ -1,7 +1,7 @@
/*
- * Copyright (c) 2000, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code 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. Oracle designates this
@@ -27,10 +27,11 @@
package java.util.logging;
import java.io.*;
import java.util.*;
import java.security.*;
+import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.net.URL;
import sun.security.action.GetPropertyAction;
@@ -152,14 +153,14 @@
private Properties props = new Properties();
private PropertyChangeSupport changes
= new PropertyChangeSupport(LogManager.class);
private final static Level defaultLevel = Level.INFO;
- // Table of known loggers. Maps names to Loggers.
- private Hashtable<String,WeakReference<Logger>> loggers =
- new Hashtable<String,WeakReference<Logger>>();
- // Tree of known loggers
+ // Table of named Loggers that maps names to Loggers.
+ private Hashtable<String,LoggerWeakRef> namedLoggers =
+ new Hashtable<String,LoggerWeakRef>();
+ // Tree of named Loggers
private LogNode root = new LogNode(null);
private Logger rootLogger;
// Have we done the primordial reading of the configuration file?
// (Must be done after a suitable amount of java.lang.System
@@ -415,10 +416,125 @@
}
return null;
}});
}
+
+ // loggerRefQueue holds LoggerWeakRef objects for Logger objects
+ // that have been GC'ed.
+ private final ReferenceQueue<Logger> loggerRefQueue
+ = new ReferenceQueue<Logger>();
+
+ // Package-level method.
+ // Helper class for managing WeakReferences to Logger objects.
+ //
+ // LogManager.namedLoggers
+ // - has weak references to all named Loggers
+ // - namedLoggers keeps the LoggerWeakRef objects for the named
+ // Loggers around until we can deal with the book keeping for
+ // the named Logger that is being GC'ed.
+ // LogManager.LogNode.loggerRef
+ // - has a weak reference to a named Logger
+ // - the LogNode will also keep the LoggerWeakRef objects for
+ // the named Loggers around; currently LogNodes never go away.
+ // Logger.kids
+ // - has a weak reference to each direct child Logger; this
+ // includes anonymous and named Loggers
+ // - anonymous Loggers are always children of the rootLogger
+ // which is a strong reference; rootLogger.kids keeps the
+ // LoggerWeakRef objects for the anonymous Loggers around
+ // until we can deal with the book keeping.
+ //
+ final class LoggerWeakRef extends WeakReference<Logger> {
+ private String name; // for namedLoggers cleanup
+ private LogNode node; // for loggerRef cleanup
+ private WeakReference<Logger> parentRef; // for kids cleanup
+
+ LoggerWeakRef(Logger logger) {
+ super(logger, loggerRefQueue);
+
+ name = logger.getName(); // save for namedLoggers cleanup
+ }
+
+ // dispose of this LoggerWeakRef object
+ void dispose() {
+ if (node != null) {
+ // if we have a LogNode, then we were a named Logger
+ // so clear namedLoggers weak ref to us
+ manager.namedLoggers.remove(name);
+ name = null; // clear our ref to the Logger's name
+
+ node.loggerRef = null; // clear LogNode's weak ref to us
+ node = null; // clear our ref to LogNode
+ }
+
+ if (parentRef != null) {
+ // this LoggerWeakRef has or had a parent Logger
+ Logger parent = parentRef.get();
+ if (parent != null) {
+ // the parent Logger is still there so clear the
+ // parent Logger's weak ref to us
+ parent.removeChildLogger(this);
+ }
+ parentRef = null; // clear our weak ref to the parent Logger
+ }
+ }
+
+ // set the node field to the specified value
+ void setNode(LogNode node) {
+ this.node = node;
+ }
+
+ // set the parentRef field to the specified value
+ void setParentRef(WeakReference<Logger> parentRef) {
+ this.parentRef = parentRef;
+ }
+ }
+
+ // Package-level method.
+ // Drain some Logger objects that have been GC'ed.
+ //
+ // drainLoggerRefQueueBounded() is called by addLogger() below
+ // and by Logger.getAnonymousLogger(String) so we'll drain up to
+ // MAX_ITERATIONS GC'ed Loggers for every Logger we add.
+ //
+ // On a WinXP VMware client, a MAX_ITERATIONS value of 400 gives
+ // us about a 50/50 mix in increased weak ref counts versus
+ // decreased weak ref counts in the AnonLoggerWeakRefLeak test.
+ // Here are stats for cleaning up sets of 400 anonymous Loggers:
+ // - test duration 1 minute
+ // - sample size of 125 sets of 400
+ // - average: 1.99 ms
+ // - minimum: 0.57 ms
+ // - maximum: 25.3 ms
+ //
+ // The same config gives us a better decreased weak ref count
+ // than increased weak ref count in the LoggerWeakRefLeak test.
+ // Here are stats for cleaning up sets of 400 named Loggers:
+ // - test duration 2 minutes
+ // - sample size of 506 sets of 400
+ // - average: 0.57 ms
+ // - minimum: 0.02 ms
+ // - maximum: 10.9 ms
+ //
+ private final static int MAX_ITERATIONS = 400;
+ final synchronized void drainLoggerRefQueueBounded() {
+ for (int i = 0; i < MAX_ITERATIONS; i++) {
+ if (loggerRefQueue == null) {
+ // haven't finished loading LogManager yet
+ break;
+ }
+
+ LoggerWeakRef ref = (LoggerWeakRef) loggerRefQueue.poll();
+ if (ref == null) {
+ break;
+ }
+ // a Logger object has been GC'ed so clean it up
+ ref.dispose();
+ }
+ }
+
/**
* Add a named logger. This does nothing and returns false if a logger
* with the same name is already registered.
* <p>
* The Logger factory methods call this method to register each
@@ -437,26 +553,30 @@
final String name = logger.getName();
if (name == null) {
throw new NullPointerException();
}
- WeakReference<Logger> ref = loggers.get(name);
+ // cleanup some Loggers that have been GC'ed
+ drainLoggerRefQueueBounded();
+
+ LoggerWeakRef ref = namedLoggers.get(name);
if (ref != null) {
if (ref.get() == null) {
- // Hashtable holds stale weak reference
- // to a logger which has been GC-ed.
- // Allow to register new one.
- loggers.remove(name);
+ // It's possible that the Logger was GC'ed after the
+ // drainLoggerRefQueueBounded() call above so allow
+ // a new one to be registered.
+ namedLoggers.remove(name);
} else {
// We already have a registered logger with the given name.
return false;
}
}
// We're adding a new logger.
// Note that we are creating a weak reference here.
- loggers.put(name, new WeakReference<Logger>(logger));
+ ref = new LoggerWeakRef(logger);
+ namedLoggers.put(name, ref);
// Apply any initial level defined for the new logger.
Level level = getLevelProperty(name+".level", null);
if (level != null) {
doSetLevel(logger, level);
@@ -467,15 +587,15 @@
loadLoggerHandlers(logger, name, name+".handlers");
processParentHandlers(logger, name);
// Find the new node and its parent.
LogNode node = findNode(name);
- node.loggerRef = new WeakReference<Logger>(logger);
+ node.loggerRef = ref;
Logger parent = null;
LogNode nodep = node.parent;
while (nodep != null) {
- WeakReference<Logger> nodeRef = nodep.loggerRef;
+ LoggerWeakRef nodeRef = nodep.loggerRef;
if (nodeRef != null) {
parent = nodeRef.get();
if (parent != null) {
break;
}
@@ -487,10 +607,13 @@
doSetParent(logger, parent);
}
// Walk over the children and tell them we are their new parent.
node.walkAndSetParent(logger);
+ // new LogNode is ready so tell the LoggerWeakRef about it
+ ref.setNode(node);
+
return true;
}
// Private method to set a level on a logger.
@@ -570,19 +693,19 @@
* <p>
* @param name name of the logger
* @return matching logger or null if none is found
*/
public synchronized Logger getLogger(String name) {
- WeakReference<Logger> ref = loggers.get(name);
+ LoggerWeakRef ref = namedLoggers.get(name);
if (ref == null) {
return null;
}
Logger logger = ref.get();
if (logger == null) {
// Hashtable holds stale weak reference
// to a logger which has been GC-ed.
- loggers.remove(name);
+ namedLoggers.remove(name);
}
return logger;
}
/**
@@ -592,11 +715,11 @@
* This method only reports on the loggers that are currently registered.
* <p>
* @return enumeration of logger name strings
*/
public synchronized Enumeration<String> getLoggerNames() {
- return loggers.keys();
+ return namedLoggers.keys();
}
/**
* Reinitialize the logging properties and reread the logging configuration.
* <p>
@@ -940,11 +1063,11 @@
}
// Nested class to represent a node in our tree of named loggers.
private static class LogNode {
HashMap<String,LogNode> children;
- WeakReference<Logger> loggerRef;
+ LoggerWeakRef loggerRef;
LogNode parent;
LogNode(LogNode parent) {
this.parent = parent;
}
@@ -956,11 +1079,11 @@
return;
}
Iterator<LogNode> values = children.values().iterator();
while (values.hasNext()) {
LogNode node = values.next();
- WeakReference<Logger> ref = node.loggerRef;
+ LoggerWeakRef ref = node.loggerRef;
Logger logger = (ref == null) ? null : ref.get();
if (logger == null) {
node.walkAndSetParent(parent);
} else {
doSetParent(logger, parent);