JDK-8161664 : Memory leak in com.apple.laf.AquaProgressBarUI: removed progress bar still referenced
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 8u92
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: os_x
  • Submitted: 2016-07-19
  • Updated: 2019-09-05
  • Resolved: 2016-08-08
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8 JDK 9
8u152Fixed 9 b133Fixed
Description
FULL PRODUCT VERSION:
java version "1.8.0_91"
Java(TM) SE Runtime Environment (build 1.8.0_91-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)

ADDITIONAL OS VERSION INFORMATION : 
Darwin saotome-57590.local 15.4.0 Darwin Kernel Version 15.4.0: Fri Feb 26 22:08:05 PST 2016; root:xnu-3248.40.184~3/RELEASE_X86_64 x86_64

A DESCRIPTION OF THE PROBLEM :
In certain scenarios, the Timer in the Animator class of the AquaProgressBarUI remains running, even when the JProgressBar is already removed from the Swing hierarchy.
This running Timer avoids that the JProgressBar can be GC-ed.

The problem can be reproduced with the following program

import java.awt.EventQueue;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;

public class AquaProgressBarUIMemoryLeakTest {

  private static JFrame sFrame;
  private static WeakReference<JProgressBar> sProgressBar;

  public static void main(String[] args) throws InvocationTargetException, InterruptedException {
    EventQueue.invokeAndWait(new Runnable() {
      @Override
      public void run() {
        showUI();
      }
    });
    //Allow the UI to be painted before disposing
    EventQueue.invokeAndWait(new Runnable() {
      @Override
      public void run() {
        disposeUI();
      }
    });

    System.runFinalization();
    System.gc();
    JProgressBar progressBar = sProgressBar.get();
    if ( progressBar != null ){
      throw new RuntimeException("Progress bar should have been GC-ed");
    }
  }

  private static void showUI(){
    sFrame = new JFrame();

    JProgressBar progressBar = new JProgressBar();
    progressBar.setVisible(false);
    progressBar.setIndeterminate(false);
    progressBar.setIndeterminate(true);
    progressBar.setIndeterminate(false);
    progressBar.setValue(10);
    progressBar.setString("Progress");

    sFrame.add(progressBar);

    sProgressBar = new WeakReference<>(progressBar);

    sFrame.setSize(200,200);
    sFrame.setVisible(true);
  }

  private static void disposeUI(){
    sFrame.setContentPane(new JPanel());
    sFrame.dispose();
    sFrame = null;
  }
}