Hi Laurent,

I checked and LoggingSupport.isAvailable() can only return false in two situations:

- no "java.util.logging.LoggingProxyImpl" class is found (but that is a permanent condition and almost all LoggingSupport methods are non-operational in that case) - there is a circularity of class initialization present and a dependent class tries to use LoggingSupport before it's static final field is set. That is only possible if java.util.logging.LoggingProxyImpl initialization caused that circularity. LoggingProxyImpl is only dependent on sun.util.logging.LoggingProxy interface which has no static initializers and LoggingProxyImpl has a simple static initializer setting static final INSTANCE field to a singleton instance and it has an empty private constructor. Therefore no class initialization circularity is possible in this situation (I think).

Regards, Peter

On 03/21/2013 03:45 PM, Peter Levart wrote:
On 03/21/2013 03:30 PM, Laurent Bourgès wrote:
Peter,

your solution looks better; I wanted my patch to be simple, efficient and only modify the JavaLogger class (localized changes).

In your patch, I have doubts related to lazy and conditional initialization in JavaLogger (static initialization):
if (LoggingSupport.isAvailable()) {
     // initialize ...
}

In original code, if LoggingSupport.isAvailable() returned false, levelObjects map remained empty and consequently null was used as the level object passed to LoggingSupport methods. In LevelEnum I try to keep this logic. When LevelEnum is first used, it's constants are initialized and level objects with them. If LoggingSupport.isAvailable() returns false, level objects are initialized to null.

I just noticed there's a bug in initialization of the LevelEnum.UNKNOWN member constant. It should not try to parse level object. Here's an update:

http://dl.dropbox.com/u/101777488/jdk8-tl/PlatformLogger/webrev.02/index.html

But your concern might be correct. In my code LevelEnum is also used from the LoggerProxy.format() method (in addition to all the places in JavaLogger) to obtain the level name for formatting. If this method is called the first time while LoggingSupport.isAvailable() returns false and that happens before JavaLogger uses LevelEnum for the first time (and at that time LoggingSupport.isAvailable() would return true), then level objects will not be initialized correctly.

Lazy initialization of level objects might help (for the price of added complexity)...

Regards, Peter

Does somebody have the knowledge about LoggingSupport.isAvailable()

and the lazy PlatformLogger initialization (JavaLogger are only used when j.u.l 
is initialized) ?


What's happening if LoggingSupport.isAvailable() returns false in your patch ?
- LevelEnum  instances are incorrectly initialized:object field is null !

- PlatformLogger is then broken ... as Level object are required by j.u.l calls


To fix both problems, moving theLevelEnum  into JavaLogger should help and 
check nulls onLevelEnum.object field.


Thanks for your feedback,
Laurent


2013/3/21 Peter Levart <peter.lev...@gmail.com <mailto:peter.lev...@gmail.com>>

    On 03/21/2013 12:12 PM, Laurent Bourgès wrote:

        Here is an improved patch tested on JDK7u13 and JDK8 internal
        build on my
        machine linux x64:
        http://jmmc.fr/~bourgesl/share/webrev-8010309/
        <http://jmmc.fr/%7Ebourgesl/share/webrev-8010309/>

        FYI, I removed completely the Map<Integer, Object>
        levelObjects and used
        two arrays to perform the PlatformLogger's level (int) to
        j.u.l.Level
        mapping:

        I decided to keep it simple as possible (no enum ...) and
        used a switch
        case based on current level occurences:


    Hi Laurent,

    In my experience enums are just the right and most compact tool
    for coding such constant associations. Here's a quick try
    (ripping off your optimized switch ;-):

    
http://dl.dropbox.com/u/101777488/jdk8-tl/PlatformLogger/webrev.01/index.html

    ...it adds 12 LOC to the original PlatformLogger and is 43 LOC
    less tha your patch. In addition:

    - only one switch instead of two (to maintain)
    - no parallel IDX_ constants

    What do you think?

    Regards, Peter



          510         /**
          511          * Return the corresponding j.u.l.Level instance
          512          * @param level PlatformLogger level as integer
          513          * @return Object (j.u.l.Level instance) or
        null if no
        matching level
          514          */
          515         private static Object getLevel(final int level) {
          516             if (levelObjects == null) {
          517                 return null;
          518             }
          519             // higher occurences first (finest, fine,
        finer, info)
          520             // based on isLoggable(level) calls
        (03/20/2013)
          521             // in jdk project only (including generated
        sources)
          522             switch (level) {
          523                 case FINEST  : return
        levelObjects[IDX_FINEST];
        // 116 + 2257 matches in generated files
          524                 case FINE    : return
        levelObjects[IDX_FINE];    // 270
          525                 case FINER   : return
        levelObjects[IDX_FINER];   // 157
          526                 case INFO    : return
        levelObjects[IDX_INFO];    // 39
          527                 case WARNING : return
        levelObjects[IDX_WARNING]; // 12
          528                 case CONFIG  : return
        levelObjects[IDX_CONFIG];  // 6
          529                 case SEVERE  : return
        levelObjects[IDX_SEVERE];  // 1
          530                 case OFF     : return
        levelObjects[IDX_OFF];     // 0
          531                 case ALL     : return
        levelObjects[IDX_ALL];     // 0
          532                 default      : return null;
          533             }
          534         }

        I enhanced the PlatformLoggerTest class also and figured out
        that TLAB
        optimized Integer allocations but I think the patch is still
        useful.

        Can you have a look to the patch ?
        Should I write a jtreg test  (performance / GC issue) ?

        Cheers,
        Laurent


        2013/3/20 Mandy Chung <mandy.ch...@oracle.com
        <mailto:mandy.ch...@oracle.com>>

              Hi Laurent,

            Thank you for signing the OCA.  Your contribution is very
            welcome.  You
            can submit a patch for this bug (see [1]) to Core
            libraries group which
            owns logging.  Jim Gish and I will sponsor it.

            Thanks
            Mandy
            [1] http://openjdk.java.net/contribute/


            On 3/20/2013 5:45 AM, Laurent Bourgès wrote:

            Hi mandy,

            Do you want me to propose an improved patch to remove the
            former Map and
            fix the getLevel() method ? or you prefer fix on your own ?

            Is it better to discuss the fix on the bug database
            (still not visible) ?

            Laurent

            2013/3/19 Mandy Chung <mandy.ch...@oracle.com
            <mailto:mandy.ch...@oracle.com>>

                  Hi Laurent,

                Thanks for the contribution.  I agree that the map
                can be replaced with a
                direct mapping from a int value to Level object and
                avoid the autoboxing
                conversion.

                I have filed a bug to track this and target this for
                JDK8:
                http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=
                8010309

                Thanks
                Mandy


                On 3/19/13 5:19 AM, Laurent Bourgès wrote:

                Dear all,

                I run recently netbeans profiler on my swing
                application (Aspro2:http://www.jmmc.fr/aspro
                ) under linux x64 platform and figured out a
                performance and waste issue related to PlatformLogger.

                Actually, the JavaLogger implementation uses a
                Map<Integer, Object>
                levelObjects to store mapping between
                PlatformLogger's levels (int) and JUL
                Level instances.

                However, the isLoggable(int level) method is highly
                used by awt project and
                other JDK projects and it leads to many Integer
                allocations as autoboxing
                converts the level as int to an Integer instance used
                by the Map.get() call.

                     /**
                      * JavaLogger forwards all the calls to its
                corresponding
                      * java.util.logging.Logger object.
                      */
                     static class JavaLogger extends LoggerProxy {
                         private static final* Map<Integer, Object>*
                levelObjects = new
                HashMap<>();
                ...
                         public boolean isLoggable(*int level*) {
                             return
                LoggingSupport.isLoggable(javaLogger, *
                levelObjects.get(level)*);
                         }
                     }

                I wrote a simple test to illustrate that performance
                / waste problem:
                PlatformLoggerTest that simply performs 1 million
                DISABLED log statements:
                             if (log.isLoggable(PlatformLogger.FINEST)) {
                                 log.finest("test
                PlatformLogger.FINEST");
                             }

                As you can see in screenshots:
                - 5 million Integer instances are allocated
                - Integer.valueOf(int) is called 5 million times
                (autoboxing)
                - HashMap.get() represents 30% of the test time
                - patched PlatformLogger is 3x times faster
                [jvm options: -Xms8m -Xmx8m -verbose:gc]

                I suggest you to use an alternative way to map
                PlatformLogger's levels
                (int) and JUL Level instances to fix that performance
                / memory issue: I
                added the getLevel(int level) method that performs a
                switch case to return
                the corresponding Level object (quick and dirty
                solution).

                I advocate this is not a very clean solution but I
                prefer efficiency here:
                any better solution may be appropriate to avoid at
                least Integer allocation
                and maybe enhance performance.

                Best regards,
                Laurent Bourgès

                PS: here is the patch as text:

                # This patch file was generated by NetBeans IDE
                # It uses platform neutral UTF-8 encoding and \n
                newlines.
                --- PlatformLogger.java (6767)
                +++ PlatformLogger.java (6768)
                @@ -468,31 +468,13 @@
                       * java.util.logging.Logger object.
                       */
                      static class JavaLogger extends LoggerProxy {
                -        /** Note: using Integer keys leads to a lot
                of new Integer
                instances !! */
                -        private static final Map<Integer, Object>
                levelObjects = new
                HashMap<>();
                -        /** fastest mapping to Level instances from
                PlatformLogger level
                as integer */
                -        private static final Object[] fastLevelObjects;
                -
                -
                +        private static final Map<Integer, Object>
                levelObjects =
                +            new HashMap<>();
                +
                          static {
                              if (LoggingSupport.isAvailable()) {
                                  // initialize the map to Level objects
                                  getLevelObjects();
                -
                -                // initialize the fastLevelObjects:
                -                fastLevelObjects = new Object[] {
                -  LoggingSupport.parseLevel(getLevelName(OFF)),   //
                0
                -  LoggingSupport.parseLevel(getLevelName(SEVERE)),    //
                1
                -  LoggingSupport.parseLevel(getLevelName(WARNING)),   //
                2
                -  LoggingSupport.parseLevel(getLevelName(INFO)),    //
                3
                -  LoggingSupport.parseLevel(getLevelName(CONFIG)),    //
                4
                -  LoggingSupport.parseLevel(getLevelName(FINE)),    //
                5
                -  LoggingSupport.parseLevel(getLevelName(FINER)),   //
                6
                -  LoggingSupport.parseLevel(getLevelName(FINEST)),    //
                7
                -  LoggingSupport.parseLevel(getLevelName(ALL))    //
                8
                -                };
                -            } else {
                -                fastLevelObjects = null; // check null
                              }
                          }

                @@ -515,7 +497,7 @@
                              this.javaLogger =
                LoggingSupport.getLogger(name);
                              if (level != 0) {
                                  // level has been updated and so
                set the Logger's level
                -  LoggingSupport.setLevel(javaLogger, getLevel(level));
                +  LoggingSupport.setLevel(javaLogger,
                levelObjects.get(level));
                              }
                          }

                @@ -526,11 +508,11 @@
                          * not be updated.
                          */
                          void doLog(int level, String msg) {
                -            LoggingSupport.log(javaLogger,
                getLevel(level), msg);
                +            LoggingSupport.log(javaLogger,
                levelObjects.get(level), msg);
                          }

                          void doLog(int level, String msg, Throwable
                t) {
                -            LoggingSupport.log(javaLogger,
                getLevel(level), msg, t);
                +            LoggingSupport.log(javaLogger,
                levelObjects.get(level), msg,
                t);
                          }

                          void doLog(int level, String msg, Object...
                params) {
                @@ -544,12 +526,12 @@
                              for (int i = 0; i < len; i++) {
                                  sparams [i] =
                String.valueOf(params[i]);
                              }
                -            LoggingSupport.log(javaLogger,
                getLevel(level), msg, sparams);
                +            LoggingSupport.log(javaLogger,
                levelObjects.get(level), msg,
                sparams);
                          }

                          boolean isEnabled() {
                              Object level =
                LoggingSupport.getLevel(javaLogger);
                -            return level == null ||
                level.equals(getLevel(OFF)) == false;
                +            return level == null ||
                level.equals(levelObjects.get(OFF)) ==
                false;
                          }

                          int getLevel() {
                @@ -566,34 +548,12 @@

                          void setLevel(int newLevel) {
                              levelValue = newLevel;
                -            LoggingSupport.setLevel(javaLogger,
                getLevel(newLevel));
                +            LoggingSupport.setLevel(javaLogger,
                levelObjects.get(newLevel));
                          }

                          public boolean isLoggable(int level) {
                -            return
                LoggingSupport.isLoggable(javaLogger, getLevel(level));
                +            return LoggingSupport.isLoggable(javaLogger,
                levelObjects.get(level));
                          }
                -
                -        /**
                -         * Return the corresponding level object
                (fastest implementation)
                -         * @param level PlatformLogger level as
                primitive integer
                -         * @return Object (JUL Level instance)
                -         */
                -        private static Object getLevel(final int
                level) {
                -            // higher occurences first (finest,
                fine, finer, info):
                -            switch (level) {
                -                case FINEST  : return
                fastLevelObjects[7];
                -                case FINE    : return
                fastLevelObjects[5];
                -                case FINER   : return
                fastLevelObjects[6];
                -                case INFO    : return
                fastLevelObjects[3];
                -                case CONFIG  : return
                fastLevelObjects[4];
                -                case WARNING : return
                fastLevelObjects[2];
                -                case SEVERE  : return
                fastLevelObjects[1];
                -                case ALL     : return
                fastLevelObjects[8];
                -                case OFF     : return
                fastLevelObjects[0];
                -                default      : return null;
                -            }
                -        }
                -
                      }

                      private static String getLevelName(int level) {








Reply via email to