Hi Kevin,
Thanks for filing both. Yes, I have a test case for the accessibility leak.
I've rerun it today against JavaFX 27.0.1 on macOS 26.6.2 (arm64) and the
result is unchanged from JavaFX 26.
The test is a window of 50 labels and 50 buttons, plus a script that reads
its accessibility tree through System Events and counts live objects with
jcmd GC.class_histogram (which runs a full GC first). Each read of an
unchanged window retains exactly 306 MacVariant and 106 long[] objects,
with no growth while idle:
after first read: MacVariant=406 long[]=2922
round 1 (+10 reads): MacVariant=3466 (+3060) long[]=3982 (+1060)
round 2 (+10 reads): MacVariant=6526 (+3060) long[]=5042 (+1060)
round 3 (+10 reads): MacVariant=9586 (+3060) long[]=6102 (+1060)
idle 30s, no reads
idle: MacVariant=9586 (+0) long[]=6102 (+0)
Steps:
1. Grant the terminal Accessibility permission (System Settings > Privacy &
Security > Accessibility).
2. Save the two files below and run chmod +x a11yleak.sh.
3. In one terminal: java --module-path <javafx-sdk>/lib --add-modules
javafx.controls --enable-native-access=javafx.graphics A11yLeak.java
It prints its PID.
4. In a second terminal: ./a11yleak.sh <pid>
----- A11yLeak.java -----
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class A11yLeak extends Application {
@Override
public void start(Stage stage) {
FlowPane pane = new FlowPane();
for(int i = 0; i < 50; i++) {
pane.getChildren().add(new Label("Label " + i));
pane.getChildren().add(new Button("Button " + i));
}
stage.setTitle("A11yLeak");
stage.setScene(new Scene(pane, 800, 400));
stage.show();
System.out.println("PID " + ProcessHandle.current().pid());
}
public static void main(String[] args) {
launch(args);
}
}
----- a11yleak.sh -----
#!/bin/sh
# Usage: a11yleak.sh <pid> [rounds] [reads-per-round]
PID=$1
ROUNDS=${2:-4}
READS=${3:-10}
counts() {
jcmd "$PID" GC.class_histogram | awk '$4 ==
"com.sun.glass.ui.mac.MacVariant" { v = $2 } $4 == "[J" { j = $2 } END {
print v + 0, j + 0 }'
}
read_tree() {
osascript -e "tell application \"System Events\" to tell (first process
whose unix id is $PID) to get entire contents of window 1" > /dev/null ||
exit 1
}
read_tree
set -- $(counts)
echo "after first read: MacVariant=$1 long[]=$2"
PREV_V=$1
PREV_J=$2
ROUND=1
while [ "$ROUND" -le "$ROUNDS" ]; do
I=0
while [ "$I" -lt "$READS" ]; do
read_tree
I=$((I + 1))
done
set -- $(counts)
echo "round $ROUND (+$READS reads): MacVariant=$1 (+$(($1 - PREV_V)))
long[]=$2 (+$(($2 - PREV_J)))"
PREV_V=$1
PREV_J=$2
ROUND=$((ROUND + 1))
done
echo "idle 30s, no reads"
sleep 30
set -- $(counts)
echo "idle: MacVariant=$1 (+$(($1 - PREV_V))) long[]=$2 (+$(($2 - PREV_J)))"
----- end -----
Some detail from the original submission that may help whoever picks it up:
- A heap dump before and after two full tree reads in my application
attributes all of the growth to JNI_LOCAL GC roots on a single thread
(MacVariant, long[] and String). These callbacks run on the toolkit thread
nested inside the event loop's native call, so their local references are
only released at exit.
- On macOS, GlassAccessible.m has no DeleteLocalRef or
PushLocalFrame/PopLocalFrame. The references come from the CallObjectMethod
results in the accessibility callbacks (accessibilityAttributeNames,
accessibilityAttributeValue, accessibilityArrayAttributeValues,
accessibilityParameterizedAttributeNames,
accessibilityAttributeValue:forParameter, accessibilityActionNames), and
from the GetObjectField and GetObjectArrayElement calls inside variantToID
and jArrayToNSArray, some of them in loops. Separately,
accessibilityAttributeValue, accessibilityAttributeValue:forParameter and
accessibilityActionDescription invoke object-returning methods with
CallLongMethod and cast the result to jobject.
- On Windows, GlassAccessible.cpp has the same gap: GetPropertyValue
(WinVariant plus the String read for VT_BSTR), callArrayMethod,
get_BoundingRectangle and get_ValueString, and it doesn't use the JLocalRef
helper used elsewhere in Windows glass. I haven't run the test case on
Windows. In my application, driven by a UI Automation client on Windows
Server 2025, live WinVariant objects grew by about 13,700 per tree read,
and after roughly two hours of automated reads the process held over 8
million WinVariant objects (452 MB).
- Bracketing each callback with PushLocalFrame/PopLocalFrame (or JLocalRef
on Windows) seems simpler than individual DeleteLocalRef calls, since the
conversions create references in loops and recursively.
- On macOS 26, Spotlight alone activates JavaFX accessibility
(JDK-8391459), so users who don't run a screen reader are affected too.
Thanks,
Craig
On Fri, Oct 30, 2026 at 12:39 AM Kevin Rushforth <[email protected]>
wrote:
> I filed both of them:
>
> https://bugs.openjdk.org/browse/JDK-8396142 : [macOS, Metal] Window
> renders blank when larger than 4096 pixels
> https://bugs.openjdk.org/browse/JDK-8396143 : Accessibility callbacks
> leak JNI local references
>
> We have all we need for the first one. A test program would help for the
> second.
>
> -- Kevin
>
>
> On 10/29/2026 3:07 PM, Kevin Rushforth wrote:
> > Hi Craig,
> >
> > I don't see your bug reports. I tried looking them up with both the
> > subject line and ID. I'll forward your email to the those responsible
> > for managing bugreport.java.com to see if they know why the bugs
> > weren't created in our system.
> >
> > In the mean time, I'll file the MetalTexture bug report myself. I can
> > reproduce it using the instructions in this email, so that is sufficient.
> >
> > I can file the accessibility JNI local references leak bug tomorrow.
> > We'll need a test case in order to address it. Do you have one?
> >
> > -- Kevin
> >
> >
> > On 10/29/2026 8:57 AM, Craig Raw wrote:
> >> Hi,
> >>
> >> I'm the developer of Sparrow Wallet, a JavaFX desktop app. I'd like
> >> to draw
> >> attention to two bugs I've submitted through bugreport.java.com, since
> >> neither has appeared in JBS yet, and the first will be encountered
> >> fairly
> >> often in the wild.
> >>
> >> 1. [macOS, Metal] Window renders blank when larger than 4096 pixels
> >> Submitted today, reference b3da673d-82e0-42a1-953a-f6b3c4209ae9
> >>
> >> With the Metal pipeline, the default on macOS from JavaFX 27, a window
> >> larger than 4096 pixels in either dimension draws nothing and stays
> >> white.
> >> MTLResourceFactory.createPresentable() calls checkTextureSize() with the
> >> window's render size, and that limit is clamped to
> >> PrismSettings.maxTextureSize, which defaults to 4096. The Metal device
> >> reports 16384. On a Retina display this affects any window wider than
> >> about
> >> 2048 points, so maximized windows on 5K and 6K displays are blank. ES2
> >> creates its swap chain without this check, so the same window renders
> >> with
> >> -Dprism.order=es2.
> >>
> >> My users started reporting this within hours of my first release on
> >> JavaFX
> >> 27.0.1. The submission includes a short standalone reproducer: run any
> >> window with -Dprism.maxTextureSize=1024 on a Retina Mac. The affected
> >> code
> >> is unchanged on master. As a workaround I'm shipping
> >> -Dprism.maxTextureSize=8192, which solves the issue.
> >>
> >> 2. Accessibility callbacks leak JNI local references
> >> Submitted 2026-09-15, reference 13f748e3-6142-4fd0-bde0-8d1d994251a2
> >>
> >> The native accessibility callbacks (mac/GlassAccessible.m,
> >> win/GlassAccessible.cpp) don't delete the JNI local references they
> >> create.
> >> Every accessibility read therefore retains MacVariant/WinVariant objects
> >> until the app exits. I reproduced it outside my app with JavaFX 26: each
> >> read of a 100-control window's accessibility tree retained 306
> >> MacVariant
> >> and 106 long[] objects. On macOS 26, Spotlight alone activates JavaFX
> >> accessibility (JDK-8391459), so this affects users who don't use a
> >> screen
> >> reader.
> >>
> >> Could someone look these submissions up? I'm happy to provide more
> >> detail
> >> or test fixes.
> >>
> >> Thanks,
> >> Craig Raw
> >
>
>