Skip to content
Permalink
Browse files
8253977: More memory leaks in client-libs on macOS
Reviewed-by: kizune
  • Loading branch information
mrserb committed Oct 7, 2020
1 parent 2a0389a commit 397307311e70f3db87ed48f5b74f9697c3bd025f
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 49 deletions.
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -224,7 +224,7 @@ protected void uninstallListeners(final AbstractButton b) {
b.removeAncestorListener(listener);
}
uninstallHierListener(b);
AquaUtilControlSize.addSizePropertyListener(b);
AquaUtilControlSize.removeSizePropertyListener(b);
}

protected void uninstallDefaults(final AbstractButton b) {
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -141,7 +141,7 @@ public void uninstallUI(final JComponent c) {
protected void installListeners() {
spinner.addPropertyChangeListener(getPropertyChangeListener());
JComponent editor = spinner.getEditor();
if (editor != null && editor instanceof JSpinner.DefaultEditor) {
if (editor instanceof DefaultEditor) {
JTextField tf = ((JSpinner.DefaultEditor)editor).getTextField();
if (tf != null) {
tf.addFocusListener(getNextButtonHandler());
@@ -151,6 +151,14 @@ protected void installListeners() {
}

protected void uninstallListeners() {
JComponent editor = spinner.getEditor();
if (editor instanceof DefaultEditor) {
JTextField tf = ((JSpinner.DefaultEditor) editor).getTextField();
if (tf != null) {
tf.removeFocusListener(getNextButtonHandler());
tf.removeFocusListener(getPreviousButtonHandler());
}
}
spinner.removePropertyChangeListener(getPropertyChangeListener());
}

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -491,12 +491,6 @@ public void updateIconImages() {
getPlatformWindow().updateIconImages();
}

@Override
public void setBackground(final Color c) {
super.setBackground(c);
updateOpaque();
}

@Override
public void setOpacity(float opacity) {
getPlatformWindow().setOpacity(opacity);
@@ -21,9 +21,14 @@
* questions.
*/

import java.awt.Component;
import java.awt.Container;
import java.awt.EventQueue;

import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
@@ -52,19 +57,33 @@ public static void main(final String[] args) throws Exception {
});
}

private static void checkListenersCount(JFileChooser chooser) {
test(chooser.getComponentListeners());
test(chooser.getFocusListeners());
test(chooser.getHierarchyListeners());
test(chooser.getHierarchyBoundsListeners());
test(chooser.getKeyListeners());
test(chooser.getMouseListeners());
test(chooser.getMouseMotionListeners());
test(chooser.getMouseWheelListeners());
test(chooser.getInputMethodListeners());
test(chooser.getPropertyChangeListeners());
test(chooser.getAncestorListeners());
test(chooser.getVetoableChangeListeners());
private static void checkListenersCount(Component comp) {
test(comp.getComponentListeners());
test(comp.getFocusListeners());
test(comp.getHierarchyListeners());
test(comp.getHierarchyBoundsListeners());
test(comp.getKeyListeners());
test(comp.getMouseListeners());
test(comp.getMouseMotionListeners());
test(comp.getMouseWheelListeners());
test(comp.getInputMethodListeners());
test(comp.getPropertyChangeListeners());
if (comp instanceof JComponent) {
test(((JComponent) comp).getAncestorListeners());
test(((JComponent) comp).getVetoableChangeListeners());
}
if (comp instanceof JMenuItem) {
test(((JMenuItem) comp).getMenuKeyListeners());
test(((JMenuItem) comp).getMenuDragMouseListeners());
}
if (comp instanceof JMenu) {
test(((JMenu) comp).getMenuListeners());
}
if (comp instanceof Container) {
for (Component child : ((Container) comp).getComponents()) {
checkListenersCount(child);
}
}
}

/**
@@ -73,7 +92,7 @@ private static void checkListenersCount(JFileChooser chooser) {
*/
private static void test(Object[] listeners) {
int length = listeners.length;
if (length > 10) {
if (length > 20) {
throw new RuntimeException("The count of listeners is: " + length);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -21,47 +21,111 @@
* questions.
*/

import java.awt.Component;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.swing.*;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDesktopPane;
import javax.swing.JEditorPane;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JProgressBar;
import javax.swing.JRadioButton;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JSlider;
import javax.swing.JSpinner;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.JToolTip;
import javax.swing.JTree;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;
import javax.swing.UIManager.LookAndFeelInfo;

import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;

import static javax.swing.UIManager.getInstalledLookAndFeels;

/**
* @test
* @key headful
* @bug 8134947
* @author Sergey Bylokhov
* @run main/timeout=300/othervm -Xmx12m -XX:+HeapDumpOnOutOfMemoryError UnninstallUIMemoryLeaks
* @bug 8134947 8253977
* @library /test/lib
* @run main/timeout=450/othervm UnninstallUIMemoryLeaks
*/
public final class UnninstallUIMemoryLeaks {

private static JFrame frame;

public static void main(final String[] args) throws Exception {
try {
createGUI();
for (final LookAndFeelInfo laf : getInstalledLookAndFeels()) {
final String name = laf.getName();
public static void main(String[] args) throws Exception {
if (args.length == 0) {
long end = System.nanoTime() + TimeUnit.SECONDS.toNanos(400);
// run one task per look and feel
List<Process> tasks = new ArrayList<>();
for (LookAndFeelInfo laf : getInstalledLookAndFeels()) {
String name = laf.getName();
if (name.contains("OS X") || name.contains("Metal")) {
SwingUtilities.invokeAndWait(() -> setLookAndFeel(laf));
SwingUtilities.invokeAndWait(() -> {
for (int i = 0; i < 4000; ++i) {
SwingUtilities.updateComponentTreeUI(frame);
}
});
tasks.add(runProcess(laf));
}
}
for (Process p : tasks) {
if (!p.waitFor(end - System.nanoTime(), TimeUnit.NANOSECONDS)) {
p.destroyForcibly();
}
}
for (Process task : tasks) {
new OutputAnalyzer(task).shouldHaveExitValue(0)
.stderrShouldBeEmpty();
}
return;
}

try {
createGUI();
long end = System.nanoTime() + TimeUnit.SECONDS.toNanos(350);
SwingUtilities.invokeAndWait(() -> {
while (end > System.nanoTime()) {
SwingUtilities.updateComponentTreeUI(frame);
}
checkListenersCount(frame);
});
} finally {
if (frame != null) { EventQueue.invokeAndWait(() -> frame.dispose()); }
if (frame != null) {EventQueue.invokeAndWait(frame::dispose);}
}
}

private static void createGUI() throws Exception {
EventQueue.invokeAndWait(() -> {
frame = new JFrame();
//TODO we sometimes generate unnecessary repaint events
frame.setIgnoreRepaint(true);
frame.setLayout(new FlowLayout());

frame.add(new JButton("JButton"));
@@ -121,13 +185,51 @@ private static void createGUI() throws Exception {
});
}

private static void setLookAndFeel(final LookAndFeelInfo laf) {
try {
UIManager.setLookAndFeel(laf.getClassName());
System.out.println("LookAndFeel: " + laf.getClassName());
} catch (ClassNotFoundException | InstantiationException |
UnsupportedLookAndFeelException | IllegalAccessException e) {
throw new RuntimeException(e);
private static void checkListenersCount(Component comp) {
test(comp.getComponentListeners());
test(comp.getFocusListeners());
test(comp.getHierarchyListeners());
test(comp.getHierarchyBoundsListeners());
test(comp.getKeyListeners());
test(comp.getMouseListeners());
test(comp.getMouseMotionListeners());
test(comp.getMouseWheelListeners());
test(comp.getInputMethodListeners());
test(comp.getPropertyChangeListeners());
if (comp instanceof JComponent) {
test(((JComponent) comp).getAncestorListeners());
test(((JComponent) comp).getVetoableChangeListeners());
}
if (comp instanceof JMenuItem) {
test(((JMenuItem) comp).getMenuKeyListeners());
test(((JMenuItem) comp).getMenuDragMouseListeners());
}
if (comp instanceof JMenu) {
test(((JMenu) comp).getMenuListeners());
}
if(comp instanceof Container) {
for(Component child: ((Container)comp).getComponents()){
checkListenersCount(child);
}
}
}

/**
* Checks the count of specific listeners, assumes that the proper
* implementation does not use more than 20 listeners.
*/
private static void test(Object[] listeners) {
int length = listeners.length;
if (length > 20) {
throw new RuntimeException("The count of listeners is: " + length);
}
}

private static Process runProcess(LookAndFeelInfo laf) throws Exception {
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
"-Dswing.defaultlaf=" + laf.getClassName(), "-mx9m",
"-XX:+HeapDumpOnOutOfMemoryError",
UnninstallUIMemoryLeaks.class.getSimpleName(), "mark");
return ProcessTools.startProcess(laf.getName(), pb);
}
}

1 comment on commit 3973073

@bridgekeeper
Copy link

@bridgekeeper bridgekeeper bot commented on 3973073 Oct 7, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.