-
Bug
-
Resolution: Duplicate
-
P2
-
None
-
5.0u8
-
x86
-
windows_xp
While investigating 6462383, I found a leak involving native cursor code in AWT. I've reproduced this on Windows XP 1.5.0_08 (b03) and later. 1.5.0_07 does not contain this bug. This bug existed in 6.0 as of b61 and before (including b51 and b45), but does not exist in b64 or later.
The bug can be reproduced with the included test case:
1) Click the "Create Big Frame" button
2) Mouse over the Big Frame - the cursor will change to crosshairs (note that changing the cursor is not necessary for demonstrating the leak, it just makes it easier to recognize)
3) Close the Big Frame using the close box
4) Repeatedly clicking the GC button shows that Big Frame's memory is still being held onto.
Note that if you close the Big Frame without moving the mouse into the client area, the leak does not happen.
Here's my test:
Leak3AWT.java
-------------
// Demonstrate leak w/ Cursor
import java.awt.event.*;
import java.awt.*;
public class Leak3AWT extends Frame implements ActionListener {
Button gcBtn;
Button awayBtn;
public Leak3AWT() {
super("Leak3AWT");
Panel btnPnl = new Panel();
btnPnl.setLayout(new FlowLayout());
gcBtn = new Button("GC");
gcBtn.addActionListener(this);
btnPnl.add(gcBtn);
awayBtn = new Button("Create Big Frame");
awayBtn.addActionListener(this);
btnPnl.add(awayBtn);
add(btnPnl, BorderLayout.SOUTH);
addWindowListener(new WL());
setFocusableWindowState(false);
}
public void actionPerformed(ActionEvent e) {
Object src = e.getSource();
if (src == gcBtn) {
System.gc();
}
else if (src == awayBtn) {
LeakFrame f = new LeakFrame(getBounds(), false);
f.setVisible(true);
}
}
static class LeakFrame extends Frame {
byte[] bigLeak;
public LeakFrame(Rectangle parentBounds, boolean cover) {
super("Big Frame");
bigLeak = new byte[1024 * 1024 * 24];
if (cover) {
setBounds(parentBounds.x, parentBounds.y,
parentBounds.width + 10,
parentBounds.height + 10);
}
else {
setBounds(parentBounds.x,
parentBounds.y + parentBounds.height + 10,
parentBounds.width + 10,
parentBounds.height + 10);
}
add(new Label("Mouse over me to leak memory"));
addWindowListener(new WL());
setFocusableWindowState(false);
setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
setVisible(true);
}
public void dispose() {
System.out.println("dispose() called");
super.dispose();
}
}
static class WL implements WindowListener {
public void windowClosed(WindowEvent e) {
}
public void windowActivated(WindowEvent e) {
}
public void windowClosing(WindowEvent e){
System.out.println("closing");
Window src = ((Window)e.getSource());
src.setVisible(false);
src.dispose();
}
public void windowDeactivated(WindowEvent e){}
public void windowDeiconified(WindowEvent e){}
public void windowIconified(WindowEvent e){}
public void windowOpened(WindowEvent e) {}
}
public static void main(String[] args) {
Leak3AWT f = new Leak3AWT();
f.pack();
f.setVisible(true);
}
}
---
YourKit indicated the reference was coming from a JNI GlobalRef. Further debugging suggested possible problems with the following native functions, which create GlobalRefs that don't appear to get Deleted:
awt_Component.cpp:
jobject AwtComponent::FindHeavyweightUnderCursor(BOOL useCache) {
...
found:
jobject localRef = comp->GetTarget(env);
--> jobject globalRef = env->NewGlobalRef(localRef);
env->DeleteLocalRef(localRef);
return globalRef;
}
awt_Cursor.cpp:
JNIEXPORT void JNICALL
Java_sun_awt_windows_WGlobalCursorManager_setCursor(JNIEnv *env, jobject,
jobject, jobject cursor, jboolean u)
{
TRY;
if (cursor != NULL) { // fix for 4430302 - getCursor() returns NULL
GlobalSetCursorStruct data;
--> data.cursor = env->NewGlobalRef(cursor);
data.u = u;
AwtToolkit::GetInstance().InvokeFunction(
GlobalSetCursor,
(void *)&data);
} else {
JNU_ThrowNullPointerException(env, "NullPointerException");
}
CATCH_BAD_ALLOC;
}
Note that unlike 6469530 (Memory leak in the focus subsystem), which only seems to leak a single Frame, this bug leaks as many Frames as you create. My test gets an OOM on the 3rd Frame.
The bug can be reproduced with the included test case:
1) Click the "Create Big Frame" button
2) Mouse over the Big Frame - the cursor will change to crosshairs (note that changing the cursor is not necessary for demonstrating the leak, it just makes it easier to recognize)
3) Close the Big Frame using the close box
4) Repeatedly clicking the GC button shows that Big Frame's memory is still being held onto.
Note that if you close the Big Frame without moving the mouse into the client area, the leak does not happen.
Here's my test:
Leak3AWT.java
-------------
// Demonstrate leak w/ Cursor
import java.awt.event.*;
import java.awt.*;
public class Leak3AWT extends Frame implements ActionListener {
Button gcBtn;
Button awayBtn;
public Leak3AWT() {
super("Leak3AWT");
Panel btnPnl = new Panel();
btnPnl.setLayout(new FlowLayout());
gcBtn = new Button("GC");
gcBtn.addActionListener(this);
btnPnl.add(gcBtn);
awayBtn = new Button("Create Big Frame");
awayBtn.addActionListener(this);
btnPnl.add(awayBtn);
add(btnPnl, BorderLayout.SOUTH);
addWindowListener(new WL());
setFocusableWindowState(false);
}
public void actionPerformed(ActionEvent e) {
Object src = e.getSource();
if (src == gcBtn) {
System.gc();
}
else if (src == awayBtn) {
LeakFrame f = new LeakFrame(getBounds(), false);
f.setVisible(true);
}
}
static class LeakFrame extends Frame {
byte[] bigLeak;
public LeakFrame(Rectangle parentBounds, boolean cover) {
super("Big Frame");
bigLeak = new byte[1024 * 1024 * 24];
if (cover) {
setBounds(parentBounds.x, parentBounds.y,
parentBounds.width + 10,
parentBounds.height + 10);
}
else {
setBounds(parentBounds.x,
parentBounds.y + parentBounds.height + 10,
parentBounds.width + 10,
parentBounds.height + 10);
}
add(new Label("Mouse over me to leak memory"));
addWindowListener(new WL());
setFocusableWindowState(false);
setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
setVisible(true);
}
public void dispose() {
System.out.println("dispose() called");
super.dispose();
}
}
static class WL implements WindowListener {
public void windowClosed(WindowEvent e) {
}
public void windowActivated(WindowEvent e) {
}
public void windowClosing(WindowEvent e){
System.out.println("closing");
Window src = ((Window)e.getSource());
src.setVisible(false);
src.dispose();
}
public void windowDeactivated(WindowEvent e){}
public void windowDeiconified(WindowEvent e){}
public void windowIconified(WindowEvent e){}
public void windowOpened(WindowEvent e) {}
}
public static void main(String[] args) {
Leak3AWT f = new Leak3AWT();
f.pack();
f.setVisible(true);
}
}
---
YourKit indicated the reference was coming from a JNI GlobalRef. Further debugging suggested possible problems with the following native functions, which create GlobalRefs that don't appear to get Deleted:
awt_Component.cpp:
jobject AwtComponent::FindHeavyweightUnderCursor(BOOL useCache) {
...
found:
jobject localRef = comp->GetTarget(env);
--> jobject globalRef = env->NewGlobalRef(localRef);
env->DeleteLocalRef(localRef);
return globalRef;
}
awt_Cursor.cpp:
JNIEXPORT void JNICALL
Java_sun_awt_windows_WGlobalCursorManager_setCursor(JNIEnv *env, jobject,
jobject, jobject cursor, jboolean u)
{
TRY;
if (cursor != NULL) { // fix for 4430302 - getCursor() returns NULL
GlobalSetCursorStruct data;
--> data.cursor = env->NewGlobalRef(cursor);
data.u = u;
AwtToolkit::GetInstance().InvokeFunction(
GlobalSetCursor,
(void *)&data);
} else {
JNU_ThrowNullPointerException(env, "NullPointerException");
}
CATCH_BAD_ALLOC;
}
Note that unlike 6469530 (Memory leak in the focus subsystem), which only seems to leak a single Frame, this bug leaks as many Frames as you create. My test gets an OOM on the 3rd Frame.
- duplicates
-
JDK-6470522 memory leak when running applet with 5u8
- Closed
- relates to
-
JDK-6469530 Memory leak in the focus subsystem
- Closed
-
JDK-6462383 Unbounded memory leak in Windows XP JRE for Applets and applications that open JFrame's
- Closed
-
JDK-6351698 Regression: 4506928 testcase is passing with 142_10-b03 but failing with 142_11-b01
- Resolved
-
JDK-6480546 JFrame does not call finalize in all cases
- Closed