Here is an improved patch tested on JDK7u13 and JDK8 internal build on my machine linux x64: http://jmmc.fr/~bourgesl/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: 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> > 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> > >> 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) { >> >> >> > >