JDK-7174718 : [macosx] Regression in 7u6 b12: PopupFactory leaks DefaultFrames.
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 7,7u6
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: os_x
  • CPU: generic,x86
  • Submitted: 2012-06-06
  • Updated: 2012-07-20
  • Resolved: 2012-07-20
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.
7u6 b17Fixed 8Fixed
Related Reports
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Relates :  
MacOS X 10.7.4 macmini4,1 and iMac

Run closed/javax/swing/Popup/DefaultFrameLeak/DefaultFrameLeak.java -- it will most probably pass on 7u6 b11 but always fail on b12 with 

java.lang.RuntimeException: FAIL: PopupFactory leaks DefaultFrames!
FAIL: Direct Popup use leaks DefaultFrames!

	at DefaultFrameLeak.onEDT1100(DefaultFrameLeak.java:176)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:601)
	at SwingTestHelper.invoke(SwingTestHelper.java:625)
	at SwingTestHelper.access$800(SwingTestHelper.java:173)
	at SwingTestHelper$6.run(SwingTestHelper.java:609)
	at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:241)
	at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:701)
	at java.awt.EventQueue.access$000(EventQueue.java:102)
	at java.awt.EventQueue$3.run(EventQueue.java:662)
	at java.awt.EventQueue$3.run(EventQueue.java:660)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:671)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:244)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:163)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:151)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:147)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:139)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:97)

EVALUATION Actually, only one more call to release is missing: we allocate a new NSWindow object and assign it to a property. We must release it manually since the property retains it automatically. After this change the PopupFactory test passes.

SUGGESTED FIX --- old/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java 2012-06-22 19:45:41.000000000 +0400 +++ new/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java 2012-06-22 19:45:41.000000000 +0400 @@ -63,6 +63,7 @@ private static native void nativeSetNSWindowSecurityWarningPositioning(long nsWindowPtr, double x, double y, float biasX, float biasY); private static native void nativeSetEnabled(long nsWindowPtr, boolean isEnabled); private static native void nativeSynthesizeMouseEnteredExitedEvents(long nsWindowPtr); + private static native void nativeDispose(long nsWindowPtr); private static native int nativeGetNSWindowDisplayID_AppKitThread(long nsWindowPtr); @@ -423,6 +424,7 @@ EventQueue.invokeLater(new Runnable() { public void run() { contentView.dispose(); + nativeDispose(getNSWindowPtr()); CPlatformWindow.super.dispose(); } }); --- old/src/macosx/native/sun/awt/AWTWindow.m 2012-06-22 19:45:42.000000000 +0400 +++ new/src/macosx/native/sun/awt/AWTWindow.m 2012-06-22 19:45:42.000000000 +0400 @@ -228,6 +228,7 @@ } if (self.nsWindow == nil) return nil; // no hope either + [self.nsWindow release]; // the property retains the object already self.isEnabled = YES; self.javaPlatformWindow = platformWindow; @@ -677,9 +678,9 @@ styleBits:styleBits frameRect:frameRect contentView:contentView]; + // the window is released is CPlatformWindow.nativeDispose() - if (window) CFRetain(window); - [window release]; // GC + if (window) CFRetain(window.nsWindow); }]; JNF_COCOA_EXIT(env); @@ -1158,5 +1159,26 @@ }]; JNF_COCOA_EXIT(env); +} + +JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeDispose +(JNIEnv *env, jclass clazz, jlong windowPtr) +{ +JNF_COCOA_ENTER(env); + + NSWindow *nsWindow = OBJC(windowPtr); + [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ + AWTWindow *window = (AWTWindow*)[nsWindow delegate]; + + // AWTWindow holds a reference to the NSWindow in its nsWindow + // property. Unsetting the delegate allows it to be deallocated + // which releases the reference. This, in turn, allows the window + // itself be deallocated. + [nsWindow setDelegate: nil]; + + [window release]; + }]; + +JNF_COCOA_EXIT(env); }

EVALUATION Certainly, some improvements to the memory management code should be done. E.g. the CPlatformWindow is a CFRetainedResource. However, the CFRetain is currently called for the AWTWindow instance, whereas at the time of disposing a window, the corresponding CFRelease will be invoked for the NSWindow object. Also, the delegate object must be released manually (note that NSWindow setDelegate does NOT retain the delegate). After making these changes I ensure that an instance of the AWTWindow class is in fact released and deallocated when Java calls removeNotify()/dispose() for a frame. And thus the javaPlatformWindow (which is a weak reference, but still) gets released as well. Also, the nsWindow property gets nil'ed, and hence doesn't hold the NSWindow object anymore. However, the NSWindow object is still retained somewhere. I presume that 2D code holds a reference to the window. This code may also be responsible for creating a global ref to its Java Window object which prevents windows from being disposed completely. We should investigate this under 7174713.

EVALUATION So far I got zero success with the proposed solution. However, there's also another issue (7174713) which seems to be realted to a memory leak caused by 2D code that operates with CALayers. These two issues may be related.

EVALUATION This is a regression caused by 7149062. Looks like the cause is that the nsWindow property is nil'ed in AWTWindow.dealloc. But dealloc is never called because the nsWindow holds a reference to the AWTWindow object (because the AWTWindow is set as a delegate for the nsWindow).