Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8145984

[macosx] sun.lwawt.macosx.CAccessible leaks

    Details

      Backports

        Description

        When a11y is in action and CAccessible objects get created they don't go away as expected and are kept held by a native code via JNIGlobalRef, at the same time leaking a peered Component (along with its whole hierarchy) via the CAccessible.accessible field.

        It can be easily reproducible with the following test case and some memory profiler (like NB or YourKit).

        1) Compile and run it with the profiler. A frame with a button appears.
        2) Activate VoiceOver a11y tool (CMD+F5).
        3) Press the button. A dialog with a text field appears.
        4) Press ENTER, the dialog gets disposed.
        5) Keep pressing the button and then ENTER for a number of times (~10).

        Expected behavior:

        All Dialog instances should be GC'ed (force GC in the profiler) when no dialog is shown.

        Actual behavior:

        Instances of the following classes are tracked in the memory dump: Dialog, JTextField, AccessibleJTextField, CAccessible. The latter is kept by a JNIGlobalRef.

        ----------------------------
        import javax.swing.*;
        import java.awt.*;

        public class Main {

            public static void main(String[] args) {
                EventQueue.invokeLater(() -> {
                    final JFrame frame = new JFrame("AX");
                    JButton button = new JButton("open");
                    button.addActionListener((e) -> {
                        new MyDialog(frame).setVisible(true);
                    });
                    frame.add(button);
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                });
            }
        }

        class MyDialog extends Dialog {

            private static int counter;

            public MyDialog(Frame owner) {
                super(owner, "Dialog");
                JTextField textField = new JTextField("" + ++counter);
                add(textField);
                textField.addActionListener((e) -> {
                    MyDialog.this.dispose();
                });
                pack();
                setLocationRelativeTo(null);
            }
        }
        ----------------------------

          Activity

          Hide
          ant Anton Tarasov added a comment -
          There're two problems here:

          1) Related native JNIGlobalRef's are not deleted when necessary.
          2) CAccessible disposal scheme looks insufficient.

          CAccessible extends CFRetainedResource which defines "dispose" method to release native CF resources. The "dispose" method is called from "finalize" only. However, a CAccessible instance itself is kept by the native resources supposed to be released on its finalization which seems a self-contained chain.
          Show
          ant Anton Tarasov added a comment - There're two problems here: 1) Related native JNIGlobalRef's are not deleted when necessary. 2) CAccessible disposal scheme looks insufficient. CAccessible extends CFRetainedResource which defines "dispose" method to release native CF resources. The "dispose" method is called from "finalize" only. However, a CAccessible instance itself is kept by the native resources supposed to be released on its finalization which seems a self-contained chain.
          Hide
          serb Sergey Bylokhov added a comment -
          Welcome! =)
          Show
          serb Sergey Bylokhov added a comment - Welcome! =)
          Hide
          ant Anton Tarasov added a comment -
          Thanks, nice to see you! ;)
          Show
          ant Anton Tarasov added a comment - Thanks, nice to see you! ;)
          Hide
          ant Anton Tarasov added a comment -
          webrev (the fix is identical on 8u/9):

          http://cr.openjdk.java.net/~ant/JDK-8145984/jdk8u/webrev.0
          http://cr.openjdk.java.net/~ant/JDK-8145984/jdk9/webrev.0

          1) JNI objects are deleted either explicitly or via Push/PopLocalFrame. However, not every call to a method producing a jobject is wrapped with Push/Pop. For instance, the following a11y methods are called directly by Cocoa and they don't delete JNI objects:

          - In JavaTextAccessibility: accessibilityValueAttribute, accessibilityIsValueAttributeSettable, accessibilityNumberOfCharactersAttribute.

          Somewhere, a JNIGlobalRef is created out of a JNILocalRef but the latter is not deleted. Provided that AWT runs an event loop, this all leak extensively when a11y is activated.

          The fix addresses these cases and actually does some additional cleanup, covering more cases not mentioned here (some cleanup may seem redundant but I did that to make sure there're no misses).

          2) java CAccessible is peered to a native JavaComponentAccessibility (via sharing a native ptr). The latter is retained with CFRetain so that its removal is triggered by CAccessible, not natively. At the same time, CAccessible is referenced via JNIGlobalRef as JavaComponentAccessibility.fAccessible (see initWithParent, where JavaComponentAccessibility.fComponent is also turns to JNIGlobalRef). As I wrote in the previous comment, CAccessible relies on "finalize" in releasing native resources.

          In order to break this chain, in the fix, an AccessibleContext is disposed explicitly on removal of the component it's associated with. AC in its turn disposes "nativeAXResource" which is set to CAccessible (the field is used on OSX only).
          Show
          ant Anton Tarasov added a comment - webrev (the fix is identical on 8u/9): http://cr.openjdk.java.net/~ant/JDK-8145984/jdk8u/webrev.0 http://cr.openjdk.java.net/~ant/JDK-8145984/jdk9/webrev.0 1) JNI objects are deleted either explicitly or via Push/PopLocalFrame. However, not every call to a method producing a jobject is wrapped with Push/Pop. For instance, the following a11y methods are called directly by Cocoa and they don't delete JNI objects: - In JavaTextAccessibility: accessibilityValueAttribute, accessibilityIsValueAttributeSettable, accessibilityNumberOfCharactersAttribute. Somewhere, a JNIGlobalRef is created out of a JNILocalRef but the latter is not deleted. Provided that AWT runs an event loop, this all leak extensively when a11y is activated. The fix addresses these cases and actually does some additional cleanup, covering more cases not mentioned here (some cleanup may seem redundant but I did that to make sure there're no misses). 2) java CAccessible is peered to a native JavaComponentAccessibility (via sharing a native ptr). The latter is retained with CFRetain so that its removal is triggered by CAccessible, not natively. At the same time, CAccessible is referenced via JNIGlobalRef as JavaComponentAccessibility.fAccessible (see initWithParent, where JavaComponentAccessibility.fComponent is also turns to JNIGlobalRef). As I wrote in the previous comment, CAccessible relies on "finalize" in releasing native resources. In order to break this chain, in the fix, an AccessibleContext is disposed explicitly on removal of the component it's associated with. AC in its turn disposes "nativeAXResource" which is set to CAccessible (the field is used on OSX only).
          Hide
          hgupdate HG Updates added a comment -
          URL: http://hg.openjdk.java.net/jdk9/dev/jdk/rev/909c7db6c47d
          User: ant
          Date: 2016-05-04 13:41:31 +0000
          Show
          hgupdate HG Updates added a comment - URL: http://hg.openjdk.java.net/jdk9/dev/jdk/rev/909c7db6c47d User: ant Date: 2016-05-04 13:41:31 +0000
          Show
          ant Anton Tarasov added a comment - webrev: http://cr.openjdk.java.net/~ant/JDK-8145984/jdk9/webrev.2 review: http://mail.openjdk.java.net/pipermail/awt-dev/2016-April/011020.html
          Hide
          hgupdate HG Updates added a comment -
          URL: http://hg.openjdk.java.net/jdk9/jdk9/jdk/rev/909c7db6c47d
          User: lana
          Date: 2016-05-11 16:05:08 +0000
          Show
          hgupdate HG Updates added a comment - URL: http://hg.openjdk.java.net/jdk9/jdk9/jdk/rev/909c7db6c47d User: lana Date: 2016-05-11 16:05:08 +0000

            People

            • Assignee:
              ant Anton Tarasov
              Reporter:
              ant Anton Tarasov
            • Votes:
              0 Vote for this issue
              Watchers:
              4 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: