src/share/classes/java/util/logging/LogManager.java

Print this page

        

*** 1,7 **** /* ! * Copyright (c) 2000, 2007, 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 --- 1,7 ---- /* ! * 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,36 **** --- 27,37 ---- 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,165 **** 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 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 --- 153,166 ---- private Properties props = new Properties(); private PropertyChangeSupport changes = new PropertyChangeSupport(LogManager.class); private final static Level defaultLevel = Level.INFO; ! // 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,424 **** --- 416,540 ---- } 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,462 **** final String name = logger.getName(); if (name == null) { throw new NullPointerException(); } ! WeakReference<Logger> ref = loggers.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); } 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)); // Apply any initial level defined for the new logger. Level level = getLevelProperty(name+".level", null); if (level != null) { doSetLevel(logger, level); --- 553,582 ---- final String name = logger.getName(); if (name == null) { throw new NullPointerException(); } ! // cleanup some Loggers that have been GC'ed ! drainLoggerRefQueueBounded(); ! ! LoggerWeakRef ref = namedLoggers.get(name); if (ref != null) { if (ref.get() == null) { ! // 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. ! 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,481 **** 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); Logger parent = null; LogNode nodep = node.parent; while (nodep != null) { ! WeakReference<Logger> nodeRef = nodep.loggerRef; if (nodeRef != null) { parent = nodeRef.get(); if (parent != null) { break; } --- 587,601 ---- loadLoggerHandlers(logger, name, name+".handlers"); processParentHandlers(logger, name); // Find the new node and its parent. LogNode node = findNode(name); ! node.loggerRef = ref; Logger parent = null; LogNode nodep = node.parent; while (nodep != null) { ! LoggerWeakRef nodeRef = nodep.loggerRef; if (nodeRef != null) { parent = nodeRef.get(); if (parent != null) { break; }
*** 487,496 **** --- 607,619 ---- 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,588 **** * <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); 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); } return logger; } /** --- 693,711 ---- * <p> * @param name name of the logger * @return matching logger or null if none is found */ public synchronized Logger getLogger(String 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. ! namedLoggers.remove(name); } return logger; } /**
*** 592,602 **** * 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(); } /** * Reinitialize the logging properties and reread the logging configuration. * <p> --- 715,725 ---- * This method only reports on the loggers that are currently registered. * <p> * @return enumeration of logger name strings */ public synchronized Enumeration<String> getLoggerNames() { ! return namedLoggers.keys(); } /** * Reinitialize the logging properties and reread the logging configuration. * <p>
*** 940,950 **** } // Nested class to represent a node in our tree of named loggers. private static class LogNode { HashMap<String,LogNode> children; ! WeakReference<Logger> loggerRef; LogNode parent; LogNode(LogNode parent) { this.parent = parent; } --- 1063,1073 ---- } // Nested class to represent a node in our tree of named loggers. private static class LogNode { HashMap<String,LogNode> children; ! LoggerWeakRef loggerRef; LogNode parent; LogNode(LogNode parent) { this.parent = parent; }
*** 956,966 **** return; } Iterator<LogNode> values = children.values().iterator(); while (values.hasNext()) { LogNode node = values.next(); ! WeakReference<Logger> ref = node.loggerRef; Logger logger = (ref == null) ? null : ref.get(); if (logger == null) { node.walkAndSetParent(parent); } else { doSetParent(logger, parent); --- 1079,1089 ---- return; } Iterator<LogNode> values = children.values().iterator(); while (values.hasNext()) { LogNode node = values.next(); ! LoggerWeakRef ref = node.loggerRef; Logger logger = (ref == null) ? null : ref.get(); if (logger == null) { node.walkAndSetParent(parent); } else { doSetParent(logger, parent);