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);