<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<p>Hi,</p>
<p>The possibility for code being called re-entrantly on the same
thread is unfortunately something that slips the minds of
developers quite easily when they develop single threaded code.
Developers assume that their private fields will be in a
consistent state because code being executed will always complete
before the same code is called again. However, in JavaFX, there
are many opportunities for 3rd party code to run via property
listeners or event handlers -- such 3rd party code can trigger
basically anything, and can sometimes lead to code in Parent to be
called again re-entrantly at a deeper stack nesting. If such a
handler is triggered at a point where the Parent class is only in
a partially consistent state, its own code will get confused. <br>
</p>
<p>This means that a class like Parent that has several
"optimizations" that make heavy use of private fields needs to be
extremely careful WHEN to make property modifications or trigger
events that may have external listeners attached to them.
Basically, any observable property should NEVER be modified when
the class is not in a fully consistent state. Simply adding a
child to the children list, or changing the parent property, must
only be done while the class is in a consistent state as who knows
what may be listening to such properties...</p>
<p>This knowledge may help you to trace the problem; look for any
property listeners or event handlers that may be directly or
indirectly triggered by something that Parent is doing (ie. a
listener on its children list, on the parent property, etc). <br>
</p>
<p>The solution IMHO is not in trying to catch exceptions and try to
recover; this is extremely hard, and I think the only possible way
to recover is to fully reset the state and peer information as if
created anew. It would likely be easier to either de-optimize the
Parent class and have it make far less assumptions (and then see
if there really was a need for these optimizations in the first
place, and redo some if needed, but with these problems in mind)
-- or identify any points where an observable property is being
modified and ensuring this is only done at safe locations when the
class is in a consistent state.<br>
<br>
--John<br>
</p>
On 25/06/2025 11:06, Dean Wookey wrote:<br>
<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>