Browse Source

Memory: Ensure UIUpdater doesn't prevent components from being garbage collected.

pull/245/head
weisj 4 years ago
parent
commit
8725b204a0
No known key found for this signature in database
GPG Key ID: 31124CB75461DA2A
  1. 16
      core/src/main/java/com/github/weisj/darklaf/listener/UIUpdater.java
  2. 43
      core/src/test/java/test/MemoryTest.java

16
core/src/main/java/com/github/weisj/darklaf/listener/UIUpdater.java

@ -22,6 +22,7 @@
package com.github.weisj.darklaf.listener; package com.github.weisj.darklaf.listener;
import java.awt.*; import java.awt.*;
import java.lang.ref.WeakReference;
import javax.swing.*; import javax.swing.*;
@ -66,11 +67,11 @@ public class UIUpdater implements ThemeChangeListener {
} }
private static void removeComponent(final JComponent component, final UIUpdater updater) { private static void removeComponent(final JComponent component, final UIUpdater updater) {
component.putClientProperty(KEY_UPDATER, null); if (component != null) component.putClientProperty(KEY_UPDATER, null);
LafManager.removeThemeChangeListener(updater); LafManager.removeThemeChangeListener(updater);
} }
private final Component component; private final WeakReference<JComponent> component;
/** /**
* Creates a new ui updater for the given component. After a new theme has been installed This * Creates a new ui updater for the given component. After a new theme has been installed This
@ -80,8 +81,8 @@ public class UIUpdater implements ThemeChangeListener {
* *
* @param component the component to update. * @param component the component to update.
*/ */
public UIUpdater(final Component component) { public UIUpdater(final JComponent component) {
this.component = component; this.component = new WeakReference<>(component);
} }
@Override @Override
@ -89,6 +90,11 @@ public class UIUpdater implements ThemeChangeListener {
@Override @Override
public void themeInstalled(final ThemeChangeEvent e) { public void themeInstalled(final ThemeChangeEvent e) {
if (component != null) SwingUtilities.updateComponentTreeUI(component); JComponent comp = component.get();
if (comp != null) {
SwingUtilities.updateComponentTreeUI(comp);
} else {
removeComponent(null, this);
}
} }
} }

43
core/src/test/java/test/MemoryTest.java

@ -32,21 +32,36 @@ import javax.swing.JButton;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JEditorPane; import javax.swing.JEditorPane;
import javax.swing.JFrame; import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JTextArea; import javax.swing.JTextArea;
import javax.swing.JTextField; import javax.swing.JTextField;
import javax.swing.JTextPane; import javax.swing.JTextPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.Timeout;
import com.github.weisj.darklaf.LafManager; import com.github.weisj.darklaf.LafManager;
import com.github.weisj.darklaf.theme.DarculaTheme; import com.github.weisj.darklaf.components.DynamicUI;
import com.github.weisj.darklaf.listener.UIUpdater;
@Timeout(value = 5) @Timeout(value = 5)
class MemoryTest { class MemoryTest {
@BeforeAll
static void setupLaf() {
TestUtils.runOnSwingThreadNotThrowing(LafManager::install);
}
@BeforeEach
void checkLaf() {
Assertions.assertTrue(LafManager.isInstalled());
}
@Test @Test
void frameGetsGarbageCollectedSimpleContent() { void frameGetsGarbageCollectedSimpleContent() {
testWithContentPane(JPanel::new); testWithContentPane(JPanel::new);
@ -75,10 +90,34 @@ class MemoryTest {
}); });
} }
@Test
void uiUpdaterReleasesMemory() {
AtomicReference<JLabel> labelRef = new AtomicReference<>();
TestUtils.runOnSwingThreadNotThrowing(() -> {
labelRef.set(new JLabel());
UIUpdater.registerComponent(labelRef.get());
});
WeakReference<JLabel> weakRef = new WeakReference<>(labelRef.get());
SwingUtilities.invokeLater(() -> labelRef.set(null));
Assertions.assertNotNull(weakRef.get());
waitForGarbageCollection(weakRef);
}
@Test
void dynamicUiReleasesMemory() {
AtomicReference<JLabel> labelRef = new AtomicReference<>();
TestUtils.runOnSwingThreadNotThrowing(
() -> labelRef.set(DynamicUI.withDynamic(new JLabel(), c -> {
})));
WeakReference<JLabel> weakRef = new WeakReference<>(labelRef.get());
SwingUtilities.invokeLater(() -> labelRef.set(null));
Assertions.assertNotNull(weakRef.get());
waitForGarbageCollection(weakRef);
}
private void testWithContentPane(final Supplier<JComponent> content) { private void testWithContentPane(final Supplier<JComponent> content) {
AtomicReference<JFrame> frame = new AtomicReference<>(); AtomicReference<JFrame> frame = new AtomicReference<>();
TestUtils.runOnSwingThreadNotThrowing(() -> { TestUtils.runOnSwingThreadNotThrowing(() -> {
LafManager.install(new DarculaTheme());
JFrame f = new JFrame(); JFrame f = new JFrame();
f.setContentPane(content.get()); f.setContentPane(content.get());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

Loading…
Cancel
Save