On Wed, 17 Jun 2026 08:54:10 GMT, Marius Hanl <[email protected]> wrote:

> This PR is an optimization for `Node.styleClass` and `Parent.stylesheets`.
> Instead of always initializing both properties with an empty list, we are 
> creating (therefore allocating) the list on the first access instead.
> Similar to many other lazy properties.
> 
> Why?
> - `Parent.getStylesheets()` is very rarely used by developers and JavaFX 
> code. So this list is very often completely unused and empty. Most developers 
> usually only add stylesheets at the `Scene`
> - `Node.getStyleClass()` is usually not used (empty) for layout containers 
> such as `Pane` or `Group`. A JavaFX App usually consists of a good amount of 
> such containers
> 
> So that our CSS code is not initializing both lists on access, I added 
> related `NodeHelper` and `ParentHelper` methods to return `null` when both 
> lists were not initialized and therefore used. Otherwise we return the list 
> as before.
> This will save us some memory and allocation, which is both good for the 
> memory consumption but also for `Node` / `Parent` creation (time).
> 
> Added documentation and tests. Will do some measurements with some apps very 
> soon and attach it here.
> 
> ---
> 
> # Benchmarks
> 
> I wrote a very small scene graph analyzer snippet to measure the memory gain.
> Feel free to test this on your own apps!
> 1. Get the `SceneGraphAnalyzer` here: 
> [SceneGraphAnalyzer](https://gist.github.com/Maran23/38beca5b043e547e1a84749e3162c0b2)
> 2. Add this code to your `Scene` and press the shortcut `F12` when all of the 
> UI is loaded:
> 
>         scene.setOnKeyPressed(event -> {
>             if (event.getCode() == KeyCode.F12) {
>                 var analyzer = new SceneGraphAnalyzer();
>                 var res = analyzer.analyze(scene);
>                 res.print();
>             }
>         });
> 
> 3. If you have a modular app, add the following VM argument: `--add-opens 
> javafx.graphics/javafx.scene=yourapp`
> 
> ## Projects
> 
> 1. Tested with 
> [JFXCentral](https://github.com/dlsc-software-consulting-gmbh/jfxcentral2)
> 
> 
> ╔══════════════════════════════════════════════════╗
> ║ Scene Graph Analysis                             ║
> ╠══════════════════════════════════════════════════╣
> ║  Total nodes:                 654                ║
> ║    ├─ Parent nodes:           388                ║
> ║    └─ Leaf nodes:             266                ║
> ╠══════════════════════════════════════════════════╣
> ║  Null styleClass:         19 /  654  (  2.9%)    ║
> ║  Null stylesheets:       386 /  388  ( 99.5%)    ║
> ╠══════════════════════════════════════════════════╣
> ║  Saved (styleClass):        1.34 K...

Today I learned about https://github.com/openjdk/jol to measure the exact 
memory footprint of objects and used those insights to measure the memory gain 
of some apps - results in the PR description.

Memory stats:


com.sun.javafx.collections.ObservableListWrapper@76bbca23d footprint:
     COUNT       AVG       SUM   DESCRIPTION
         1        16        16   [Ljava.lang.Object;
         1        40        40   
com.sun.javafx.collections.ObservableListWrapper
         1        24        24   java.util.ArrayList
         3                  80   (total)

With `-XX:+UseCompactObjectHeaders` (always enabled in JDK-27, so used this 
value as baseline):

com.sun.javafx.collections.ObservableListWrapper@7ffb7d94d footprint:
     COUNT       AVG       SUM   DESCRIPTION
         1        16        16   [Ljava.lang.Object;
         1        32        32   
com.sun.javafx.collections.ObservableListWrapper
         1        24        24   java.util.ArrayList
         3                  72   (total)

-------------

PR Comment: https://git.openjdk.org/jfx/pull/2191#issuecomment-4735606589

Reply via email to