JDK-6802944 : Nimbus initialization is too slow
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 6u14
  • Priority: P1
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2009-02-09
  • Updated: 2011-01-25
  • Resolved: 2009-05-18
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6 JDK 7 Other
6u14 b03Fixed 7Fixed OpenJDK6Fixed
Related Reports
Relates :  
Relates :  
Description
It was shown that avoiding Nimbus initialization saves about 10% of startup of simple FX apps.
E.g. here are data from Dmitry's experiment:

> In the meantime, the difference between initializing Nimbus L&F and not during startup is about 11% on my system for FXFramer: 2.24s vs 2.0s . 

This is significant chunk of time. Expecially because many FX applications barely need Nibmus as they do not use standard Swing components. 

There are some efforts to delay nimbus initialization on FX side. But these are workarounds and it will be always possible to defeat them. Nimbus startup has to be optimized.

Comments
EVALUATION These changes improved performance of a simple test (set Nimbus, show a frame) by 14%. Figures as measured by refworkload: Stock Nimbus: 1.07 sec Optimized Nimbus: 0.92 sec Metal: 0.73 sec
26-02-2009

EVALUATION compileDefaults() can be rewritten using entrySet() instead of keySet(). It creates a set of TreeMaps which cannot resolve lazy values, so extra care should be taken when getting values from this map. Ideally UIDefaults should compile these by-prefix maps on the fly, then there would be no need to scan the whole table each time a property is added. I have some ideas to try, but they're for jdk7. DerivedColors could be updated more effectively if organized into a tree. It is that tree, and not individual colors, that should listen to color changes. It would then just rederive the updated color and all its children. So i introduce a new class, ColorTree, to NimbusDefaults with methods to add/traverse/update colors. This tree listens to changes to UIDefaults properly. Some changes to generator files are needed, too.
26-02-2009

SUGGESTED FIX compileDefaults() can be rewritten using entrySet() instead of keySet(). It creates a set of TreeMaps which cannot resolve lazy values, so extra care should be taken when getting values from this map. Ideally UIDefaults should compile these by-prefix maps on the fly, then there would be no need to scan the whole table each time a property is added. I have some ideas to try, but they're for jdk7. DerivedColors could be updated more effectively if organized into a tree. It is that tree, and not individual colors, that should listen to color changes. It would then just rederive the updated color and all its children. So i introduce a new class, ColorTree, to NimbusDefaults with methods to add/traverse/update colors. This tree listens to changes to UIDefaults properly. Some changes to generator files are needed, too.
18-02-2009

EVALUATION I've tried to profile following minimal example and found that there are number of things that can be improved: ========== import javax.swing.JFrame; import javax.swing.UIManager; public class NimbusTest { public static void main(String[] args) throws Exception { UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"); JFrame f = new JFrame("Test"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); System.err.println("Done"); } } =========== 1) Nimbus does "compile" resources to hasmap different from UIDefaults. Comment says it is done for performance reasons to avoid impact on miss lookups. I do not exactly understand this but the way NimbusStyle.compileDefaults() is implemented is wrong as it causes loading of ALL lazy values. Rewriting this along following lines cuts time significantly (note that it is not ideal fix yet as dereferencing lazy value will cause its replacement in the UIDefaults but not its update in this compiled representation). private void compileDefaults( Map<String, TreeMap<String,Object>> compiledDefaults, UIDefaults d) { for (Map.Entry obj : d.entrySet()) { Object k = obj.getKey(); if (k instanceof String) { String key = (String) k; String kp = parsePrefix(key); if (kp == null) continue; TreeMap<String,Object> map = compiledDefaults.get(kp); if (map == null) { map = new TreeMap<String,Object>(); compiledDefaults.put(kp, map); } map.put(key, obj.getValue()); } } } 2) derivedColor and derivedColorKey may cache hashcode in the transient variable. It is also helpful to check for equivalence of hashcodes in the equals(). There are lots of hashmap lookups related to derivedkey object and it saves some time. For DerivedColorKey it has sense to save int representations of the fields instead of floats as it will save subsequent hashcode() and equals() and noone actually needs their float versions. 3) NimbusDefaults.DefaultsListener.propertyChange() is not optimal as it triggers a lot of not needed events Keeping list of colors ordered (the only requirement is that "parent" color has to be in front of any of its childs) will help to ensure that all changes could be done by single iteration over array. (Note that you have to be careful with events here - e.g. you may want avoid posting events to same default listener). Also, when color is created if LAF was not set yet then there is no need to fire events or even rederiveColor(). When LAF will be set all of them will be rederived again. According to profiler these changes help to cut cost of Nimbus initialization to 20% of initial. Perhaps something more can be done if NibusDefaults will be improved.
09-02-2009