<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<p>I've looked at the code as well, and I think I agree with your
assessment. `getChildTransformedBounds` does a lot of stuff, and
will modify properties that can be observed while the
`dirtyChildren` list is being used in `updateCachedBounds`. <br>
</p>
<p>However, what I think the problem is directly with the
`remainingDirtyNodes` counter. This is an optimization to avoid
checking all children if the `children` list is passed (instead of
`dirtyChildren`) to updateCachedBounds. However, if while
iterating LESS children are found with their bounds changed
(because some of them were fixed already) then the
`remainingDirtyNodes` counter can no longer be correct, and the
loop continues until it hits negative indices.</p>
<p>I see a few possible solutions:</p>
<p>- Simply add `i >= 0` as a condition to the `for` loop, and
using `remainingDirtyNodes` more as a "rough indication" instead
of an exact truth; the reason why I think that's a reasonable fix
is that even without using `remainingDirtyNodes` the loop would
function correctly (albeit perhaps slower in some cases as it
checks too much). The optimization here is assuming that children
are usually appended at the end of the list (which is not always
the case) and once you have encountered X dirty nodes, you can
exit early, avoiding having to iterate potentially large children
lists fully.</p>
<p>- Completely remove `remainingDirtyNodes` and
`dirtyChildrenCount` -- this would potentially slow down
updateCachedBounds for nodes with many children (likely
unnoticable though until you hit 1000+ children under a single
node -- something to be avoided for far more reasons than just
this code). The loop would then simply check all children (or
only the dirtyChildren).</p>
<p>Although I'm very sure these solutions will made the IOOBE
disappear, I'm not 100% sure it may not expose a new problem (a
node with perhaps bounds that weren't updated) -- however, seeing
this new problem will allow us to actually work on a solution
instead of not even getting to that point because of an exception
that cripples the whole application with no recourse...</p>
<p>Making a copy of the Parent class (in its original package) and
including it in your project (perhaps having to open some modules
or disable the module system) and adding an `i >= 0` condition
should probably resolve this problem if you want to experiment
with this solution. This is how I usually debug problems and try
out solutions without having to build a complete new set of FX
artifacts.</p>
<p>I tried making defensive copies of the List passed in, but this
was insufficient (as the problem is not with the list becoming out
of date, but with the bounds changed flag changing without a
corresponding update to `remainingDirtyNodes`).<br>
</p>
<p>--John<br>
</p>
<p>On 25/06/2025 11:06, Dean Wookey wrote:</p>
<blockquote type="cite"
cite="mid:CAKZ_BFhxTEuamPe7i4JFsuRSiA3r3O+z20j9rAaf93ssSAXjOg@mail.gmail.com">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<div dir="ltr">
<ol
style="margin:0px;list-style:none;padding:0px;color:rgb(23,43,77);font-family:"DejaVu Sans",sans-serif;font-size:14px">
Hi Everyone,<br>
<br>
We've also been experiencing this problem over the years. It
seems to be related to JDK-8198577.<br>
<br>
Once it goes wrong, each pulse hits the issue repeated meaning
it can never escape. It's rare, but extremely disruptive when
it does occur because the user loses what they've been working
on and has to restart the app.<br>
<br>
I've tried really hard to figure out the conditions this
happens in. I don't think it's a multiple thread issue
(although for some people it almost certainly could be
triggered that way) because we've put conditional breakpoints
that trigger whenever anything that could affect dirty
children is done off the app thread. We've got assert
Platform.isFXApplicationThread() all over our app to make sure
the threading is happening properly.<br>
<br>
What I think is happening is that getChildTransformedBounds
which is being called inside the updateCachedBounds loop, can
in some rare cases, end up triggering a call to
updateCachedBounds on the same node. Basically
updateCachedBounds can call itself recursively. This is a
snipped from Parent.java in updateCachedBounds.<br>
<br>
// this checks the newly added nodes first, so if dirtyNodes
is the<br>
// whole children list, we can end early<br>
for (int i = dirtyNodes.size() - 1; remainingDirtyNodes >
0; --i) {<br>
final Node node = dirtyNodes.get(i);<br>
if (node.boundsChanged) {<br>
// assert node.isVisible();<br>
node.boundsChanged = false;<br>
--remainingDirtyNodes;<br>
tmp = getChildTransformedBounds(node,
BaseTransform.IDENTITY_TRANSFORM, tmp);<br>
<br>
In the code above, if this gets called recursively through
getChildTransformedBounds, then node.boundsChanged will change
to false for all the nodes which stops remainingDirtyNodes
from being updated and i eventually goes negative.<br>
<br>
We tried to fix the scene graph when this happens by catching
the exception in the Thread.setDefaultUncaughtExceptionHandler
but it didn't work. Maybe Christopher's suggested fix would
work, but as Kevin says "It needs to be tested to ensure that
when we get the AIOOBE that we can recover. It wouldn't solve
anything if we catch and log that exception only to have it
fail shortly after because the scene graph isn't in a good
state (I don't know whether that would be the case, but it's
something that needs to be checked)."<br>
<br>
Here's how we tried to fix the scene graph when we caught the
error. The "Fixing IOB Issue" log gets hit all the time, but
it doesn't find any problems, and in the next pulse it hits
the problem again with various different stack traces until it
settles on one. In our latest example of the error, it first
occurred during a Platform.runLater and not during the pulse,
but then all subsequent issues happen during the pulse.<br>
<br>
protected static void checkSpecialException(Throwable t) {<br>
if (t instanceof IndexOutOfBoundsException) {<br>
fixIndexOutOfBounds(t);<br>
}<br>
}<br>
<br>
public static void fixIndexOutOfBounds(Throwable
throwable) {<br>
FXUtilities.log(EmbraceDesktop.class, <a
href="http://org.slf4j.event.Level.INFO"
moz-do-not-send="true">org.slf4j.event.Level.INFO</a>,
"Fixing IOB Issue");<br>
try {<br>
Field dirtyChildrenCountField =
Parent.class.getDeclaredField("dirtyChildrenCount");<br>
dirtyChildrenCountField.setAccessible(true);<br>
Field dirtyChildrenField =
Parent.class.getDeclaredField("dirtyChildren");<br>
dirtyChildrenField.setAccessible(true);<br>
Set<Scene> apps =
applicationManager.getApplications();<br>
ArrayList<Node> brokenStack = new
ArrayList<>();<br>
for (Scene s: apps) {<br>
fixTreeRecursive(dirtyChildrenCountField,
dirtyChildrenField, s.getRoot(), brokenStack);<br>
}<br>
if (brokenStack.size() > 0) {<br>
StringBuilder errorStack = new
StringBuilder();<br>
for (Node n: brokenStack) {<br>
errorStack.append(n.getClass().getSimpleName() + " " +
String.join( ",", n.getStyleClass())).append("\n");<br>
}<br>
EmbraceAnalytics.logCrash("Index out of bounds
crash",errorStack.toString(), throwable);<br>
}<br>
<br>
<br>
}<br>
catch (Throwable t2) {<br>
FXUtilities.log(EmbraceDesktop.class,
org.slf4j.event.Level.ERROR, "Exception while fixing tree",
t2);<br>
}<br>
}<br>
<br>
protected static boolean fixTreeRecursive(Field
dirtyChildrenCountField, Field dirtyChildrenField, Parent
parent, ArrayList<Node> brokenStack) throws
IllegalAccessException {<br>
List<?> dirtyChildren = (List<?>)
dirtyChildrenField.get(parent);<br>
int dirtyChildrenCount = (int)
dirtyChildrenCountField.get(parent);<br>
if (dirtyChildren != null) {<br>
if (dirtyChildrenCount > dirtyChildren.size())
{<br>
FXUtilities.log(EmbraceDesktop.class,
org.slf4j.event.Level.ERROR, "Offending node1 was " +
parent.getClass().getSimpleName());<br>
dirtyChildrenCountField.set(parent,
dirtyChildren.size());<br>
brokenStack.add(parent);<br>
return true;<br>
}<br>
}<br>
else {<br>
if (parent.getChildrenUnmodifiable().size() <
dirtyChildrenCount) {<br>
FXUtilities.log(EmbraceDesktop.class,
org.slf4j.event.Level.ERROR, "Offending node2 was " +
parent.getClass().getSimpleName());<br>
dirtyChildrenCountField.set(parent,
parent.getChildrenUnmodifiable().size());<br>
brokenStack.add(parent);<br>
return true;<br>
}<br>
}<br>
for (Node n: parent.getChildrenUnmodifiable()) {<br>
if (n instanceof Parent) {<br>
boolean error =
fixTreeRecursive(dirtyChildrenCountField, dirtyChildrenField,
(Parent)n, brokenStack);<br>
if (error) {<br>
brokenStack.add(parent);<br>
FXUtilities.log(EmbraceDesktop.class,
org.slf4j.event.Level.ERROR, "Parent was " +
parent.getClass().getSimpleName());<br>
}<br>
return error;<br>
}<br>
}<br>
return false;<br>
}<br>
<br>
I think we should we should put the index check potential fix
in and log when it happens. As far as we can tell, if this
issue gets hit, it's catastrophic 100% of the time. The fix
might resolve the issue. It can't really make it any worse.
Another thing we should do is add a check for recursive entry
to that method and log when that occurs. That's (I think) the
real issue, and without a stack trace of that, it's hard to
find the root cause.<br>
<br>
I don't know if anyone else has experienced this issue and has
insights/workarounds?<br>
<br>
Dean<br>
</ol>
</div>
<br>
<div class="gmail_quote">
<div dir="ltr" class="gmail_attr">On Mon, Mar 24, 2025 at
5:22 PM Christopher Schnick <<a
href="mailto:crschnick@xpipe.io" target="_blank"
moz-do-not-send="true" class="moz-txt-link-freetext">crschnick@xpipe.io</a>>
wrote:<br>
</div>
<blockquote class="gmail_quote"
style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div>
<p>Hello,<br>
<br>
We encountered an issue after updating our application
implementation to frequently change the visibility of
nodes. We are essentially now running an implementation
that very frequently changes the visibility of various
children nodes based on when they are needed and shown.
When the user performs a lot of actions, the visibility of
many nodes will be changed rapidly. <br>
<br>
For that, there are many listeners in place that listen
for bounds changes of nodes to recheck whether they need
to be made visible or not. All the visibility changes are
queued up, so they are not immediately done in the
listener after any bounds changes of parents. They are all
properly done on the platform thread with runLater. When
this implementation is running on many client systems, we
sometimes receive an error report with an exception that
looks something like this:<br>
<br>
java.lang.IndexOutOfBoundsException: Index -1 out of
bounds for length 2<br>
at
java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:100)<br>
at
java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:106)<br>
at
java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:302)<br>
at
java.base/java.util.Objects.checkIndex(Objects.java:365)<br>
at
java.base/java.util.ArrayList.get(ArrayList.java:428)<br>
at
<a
href="mailto:javafx.base@25-ea/com.sun.javafx.collections.ObservableListWrapper.get"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.base@25-ea/com.sun.javafx.collections.ObservableListWrapper.get</a>(ObservableListWrapper.java:88)<br>
at
<a
href="mailto:javafx.base@25-ea/com.sun.javafx.collections.VetoableListDecorator.get"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.base@25-ea/com.sun.javafx.collections.VetoableListDecorator.get</a>(VetoableListDecorator.java:326)<br>
at
<a
href="mailto:javafx.graphics@25-ea/javafx.scene.Parent.updateCachedBounds"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/javafx.scene.Parent.updateCachedBounds</a>(Parent.java:1769)<br>
at
<a
href="mailto:javafx.graphics@25-ea/javafx.scene.Parent.recomputeBounds"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/javafx.scene.Parent.recomputeBounds</a>(Parent.java:1713)<br>
at
<a
href="mailto:javafx.graphics@25-ea/javafx.scene.Parent.doComputeGeomBounds"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/javafx.scene.Parent.doComputeGeomBounds</a>(Parent.java:1566)<br>
at
<a
href="mailto:javafx.graphics@25-ea/javafx.scene.Parent$1.doComputeGeomBounds"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/javafx.scene.Parent$1.doComputeGeomBounds</a>(Parent.java:116)<br>
at
<a
href="mailto:javafx.graphics@25-ea/com.sun.javafx.scene.ParentHelper.computeGeomBoundsImpl"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/com.sun.javafx.scene.ParentHelper.computeGeomBoundsImpl</a>(ParentHelper.java:84)<br>
at
<a
href="mailto:javafx.graphics@25-ea/com.sun.javafx.scene.layout.RegionHelper.superComputeGeomBoundsImpl"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/com.sun.javafx.scene.layout.RegionHelper.superComputeGeomBoundsImpl</a>(RegionHelper.java:78)<br>
at
<a
href="mailto:javafx.graphics@25-ea/com.sun.javafx.scene.layout.RegionHelper.superComputeGeomBounds"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/com.sun.javafx.scene.layout.RegionHelper.superComputeGeomBounds</a>(RegionHelper.java:62)<br>
at
<a
href="mailto:javafx.graphics@25-ea/javafx.scene.layout.Region.doComputeGeomBounds"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/javafx.scene.layout.Region.doComputeGeomBounds</a>(Region.java:3301)<br>
at
<a
href="mailto:javafx.graphics@25-ea/javafx.scene.layout.Region$1.doComputeGeomBounds"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/javafx.scene.layout.Region$1.doComputeGeomBounds</a>(Region.java:166)<br>
at
<a
href="mailto:javafx.graphics@25-ea/com.sun.javafx.scene.layout.RegionHelper.computeGeomBoundsImpl"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/com.sun.javafx.scene.layout.RegionHelper.computeGeomBoundsImpl</a>(RegionHelper.java:89)<br>
at
<a
href="mailto:javafx.graphics@25-ea/com.sun.javafx.scene.NodeHelper.computeGeomBounds"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/com.sun.javafx.scene.NodeHelper.computeGeomBounds</a>(NodeHelper.java:101)<br>
at <a
href="mailto:javafx.graphics@25-ea/javafx.scene.Node.updateGeomBounds"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/javafx.scene.Node.updateGeomBounds</a>(Node.java:3908)<br>
at <a
href="mailto:javafx.graphics@25-ea/javafx.scene.Node.getGeomBounds"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/javafx.scene.Node.getGeomBounds</a>(Node.java:3870)<br>
at <a
href="mailto:javafx.graphics@25-ea/javafx.scene.Node.getLocalBounds"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/javafx.scene.Node.getLocalBounds</a>(Node.java:3818)<br>
at <a
href="mailto:javafx.graphics@25-ea/javafx.scene.Node.updateTxBounds"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/javafx.scene.Node.updateTxBounds</a>(Node.java:3972)<br>
at
<a
href="mailto:javafx.graphics@25-ea/javafx.scene.Node.getTransformedBounds"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/javafx.scene.Node.getTransformedBounds</a>(Node.java:3764)<br>
at <a
href="mailto:javafx.graphics@25-ea/javafx.scene.Node.updateBounds"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/javafx.scene.Node.updateBounds</a>(Node.java:828)<br>
at <a
href="mailto:javafx.graphics@25-ea/javafx.scene.Parent.updateBounds"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/javafx.scene.Parent.updateBounds</a>(Parent.java:1900)<br>
at
<a
href="mailto:javafx.graphics@25-ea/javafx.scene.Scene$ScenePulseListener.pulse"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/javafx.scene.Scene$ScenePulseListener.pulse</a>(Scene.java:2670)<br>
at
<a
href="mailto:javafx.graphics@25-ea/com.sun.javafx.tk.Toolkit.runPulse"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/com.sun.javafx.tk.Toolkit.runPulse</a>(Toolkit.java:380)<br>
at
<a
href="mailto:javafx.graphics@25-ea/com.sun.javafx.tk.Toolkit.firePulse"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/com.sun.javafx.tk.Toolkit.firePulse</a>(Toolkit.java:401)<br>
at
<a
href="mailto:javafx.graphics@25-ea/com.sun.javafx.tk.quantum.QuantumToolkit.pulse"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/com.sun.javafx.tk.quantum.QuantumToolkit.pulse</a>(QuantumToolkit.java:592)<br>
at
<a
href="mailto:javafx.graphics@25-ea/com.sun.javafx.tk.quantum.QuantumToolkit.pulse"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/com.sun.javafx.tk.quantum.QuantumToolkit.pulse</a>(QuantumToolkit.java:572)<br>
at
<a
href="mailto:javafx.graphics@25-ea/com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue</a>(QuantumToolkit.java:565)<br>
at
<a
href="mailto:javafx.graphics@25-ea/com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$6"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$6</a>(QuantumToolkit.java:346)<br>
at
<a
href="mailto:javafx.graphics@25-ea/com.sun.glass.ui.InvokeLaterDispatcher$Future.run$$$capture"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/com.sun.glass.ui.InvokeLaterDispatcher$Future.run$$$capture</a>(InvokeLaterDispatcher.java:95)<br>
at
<a
href="mailto:javafx.graphics@25-ea/com.sun.glass.ui.InvokeLaterDispatcher$Future.run"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">javafx.graphics@25-ea/com.sun.glass.ui.InvokeLaterDispatcher$Future.run</a>(InvokeLaterDispatcher.java)<br>
<br>
The index out of bounds is not always the same, there are
various variations of this. It happens on all operating
systems. It seems like there is a very specific scenario
where an index can be out of bounds. This happens very
rarely, like only a few times out of some hundred
application runs, so I tried my best at forcing it to
reproduce.<br>
<br>
The following reproducer works most of the time, but it
might have to be run multiple times. I am aware that it
eventually results in a StackOverflow, but that was the
best way to force it reliably, by just continuously
spamming visibility changes to eventually encounter this
rare issue. But I want to emphasize that the same error
also occurs naturally when not being forced like this, but
it is just a lot more rare. So the StackOverflow in the
reproducer has nothing to do with this issue, it also
happens later on.</p>
<div
style="background-color:rgb(30,31,34);color:rgb(188,190,196)">
<pre
style="font-family:"JetBrains Mono",monospace"><span
style="color:rgb(207,142,109)">import </span>javafx.application.Application;
<span style="color:rgb(207,142,109)">import </span>javafx.scene.Scene;
<span style="color:rgb(207,142,109)">import </span>javafx.scene.control.Button;
<span style="color:rgb(207,142,109)">import </span>javafx.scene.layout.Region;
<span style="color:rgb(207,142,109)">import </span>javafx.scene.layout.StackPane;
<span style="color:rgb(207,142,109)">import </span>javafx.scene.layout.VBox;
<span style="color:rgb(207,142,109)">import </span>javafx.stage.Stage;
<span style="color:rgb(207,142,109)">import </span>java.io.IOException;
<span style="color:rgb(207,142,109)">public class </span>ParentBoundsBug <span
style="color:rgb(207,142,109)">extends </span>Application {
<span style="color:rgb(179,174,96)">@Override
</span><span style="color:rgb(179,174,96)"> </span><span
style="color:rgb(207,142,109)">public void </span><span
style="color:rgb(86,168,245)">start</span>(Stage stage) <span
style="color:rgb(207,142,109)">throws </span>IOException {
Scene scene = <span style="color:rgb(207,142,109)">new </span>Scene(createContent(), <span
style="color:rgb(42,172,184)">640</span>, <span
style="color:rgb(42,172,184)">480</span>);
stage.setScene(scene);
stage.show();
stage.centerOnScreen();
}
<span style="color:rgb(207,142,109)">private </span>Region <span
style="color:rgb(86,168,245)">createContent</span>() {
<span style="color:rgb(207,142,109)">var </span>b1 = <span
style="color:rgb(207,142,109)">new </span>Button(<span
style="color:rgb(106,171,115)">"Click me!"</span>);
<span style="color:rgb(207,142,109)">var </span>b2 = <span
style="color:rgb(207,142,109)">new </span>Button(<span
style="color:rgb(106,171,115)">"Click me!"</span>);
<span style="color:rgb(207,142,109)">var </span>vbox = <span
style="color:rgb(207,142,109)">new </span>VBox(b1, b2);
b1.boundsInParentProperty().addListener((observable, oldValue, newValue) -> {
<span style="color:rgb(199,125,187)">vbox</span>.setVisible(!<span
style="color:rgb(199,125,187)">vbox</span>.isVisible());
});
b2.boundsInParentProperty().addListener((observable, oldValue, newValue) -> {
<span style="color:rgb(199,125,187)">vbox</span>.setVisible(!<span
style="color:rgb(199,125,187)">vbox</span>.isVisible());
});
vbox.boundsInParentProperty().addListener((observable, oldValue, newValue) -> {
<span style="color:rgb(199,125,187)">vbox</span>.setVisible(!<span
style="color:rgb(199,125,187)">vbox</span>.isVisible());
});
<span style="color:rgb(207,142,109)">var </span>stack = <span
style="color:rgb(207,142,109)">new </span>StackPane(vbox, <span
style="color:rgb(207,142,109)">new </span>StackPane());
stack.boundsInParentProperty().addListener((observable, oldValue, newValue) -> {
<span style="color:rgb(199,125,187)">vbox</span>.setVisible(!<span
style="color:rgb(199,125,187)">vbox</span>.isVisible());
});
<span style="color:rgb(207,142,109)">return </span>stack;
}
<span style="color:rgb(207,142,109)">public static void </span><span
style="color:rgb(86,168,245)">main</span>(String[] args) {
<span style="font-style:italic">launch</span>();
}
}</pre>
</div>
<p><br>
It doesn't necessarily have something to do with running
the visibility change directly in the listener, our
application does a runLater to change the visibility
state, still with the same results. To properly debug
this, you will have to launch the reproducer with a bigger
stack size like -Xss8m to increase the chance that it
occurs. Then, you can just set a breakpoint at
jdk.internal.util.Preconditions:302, and wait for it to
trigger the OOB eventually.<br>
<br>
This problem is currently the biggest JavaFX issue for us
as it breaks the layout and usually requires a restart to
fix. <br>
<br>
Looking at the bounds calculation code, the list index
bounds check is very optimistic in that it doesn't check
any indices and relies on multiple assumtions to hold. So
if it is very difficult to find the cause, a simple index
bounds check for the list access would also work fine.</p>
<p>Best<br>
Christopher Schnick<br>
</p>
</div>
</blockquote>
</div>
</blockquote>
</body>
</html>