Я провел последние 2 дня, пытаясь отладить утечку памяти в моем приложении, и теперь сузил ее до одного JFrame, который не удаляется должным образом.
Запускается из контрольного фрейма с помощью следующего фрагмента:
startButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new LeakingInterface();
}
});
В самом фрейме установлено setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
.
Содержит кнопку, которая сохраняет изменения, внесенные в рамку, а затем удаляет их, а также обычную кнопку закрытия.Кнопка «сохранить изменения» сформулирована следующим образом:
saveChanges.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
saveChanges();
dispose();
}
});
Теперь к моей проблеме: Кадр неправильно выгружен из памяти.Я отлаживал это с помощью анализатора памяти Eclipse.При закрытии фрейма с помощью кнопки закрытия, предоставленной операционной системой, ни один из фреймов не выгружается / не удаляется, на экране «вероятные утечки» после и закрытия фрейма говорится 11 раз:
11 instances of "LeakingInterface", loaded by "sun.misc.Launcher$AppClassLoader @ 0x6c00f08b8" occupy 393.71 MB (97,10%) bytes.
These instances are referenced from one instance of "java.lang.Thread", loaded by "<system class loader>"
Довольно странно закрытиеЭкраны с помощью кнопки «Сохранить изменения» выгружают некоторые JFrames, а экран «Вероятные утечки» дает мне разные ответы.Снова для 11 загруженных и закрытых кадров:
One instance of "LeakingInterface" loaded by "sun.misc.Launcher$AppClassLoader @ 0x6c01bc1f8" occupies 36.21 MB (19,20%) bytes. The memory is accumulated in one instance of "LeakingInterface" loaded by "sun.misc.Launcher$AppClassLoader @ 0x6c01bc1f8".
Это сообщение появляется 5 раз, но только 5 раз, и занятая память намного меньше, чем закрытие кнопкой выхода ОС.
I 'м, в конце концов я сообразил, что вызывает это и как я могу это исправить.Отладка была выполнена в Mac OS, но такое же поведение (утечка памяти) можно увидеть и в Windows.
То, что я пробовал, не дало результата:
1) Преобразование «LeakingInterface»в одноэлементный класс, подобный этому:
public static LeakingInterface showCardChangeInterface(){
if(instance != null){
instance.dispose();
instance = null;
System.gc(); //Trying to make sure the gc runs at least once
}
instance = new LeakingInterface();
return instance;
}
2) Вместо того, чтобы использовать просто конструктор для показа фрейма, вместо этого преобразуйте фрейм в поле и убедитесь, что он правильно обнулен и расположен перед повторным открытием:
startButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if(leakingFrame != null){
leakingFrame.dispose();
}
leakingFrame = new LeakingFrame();
}
});
3) Пытаясь правильно скопировать хотя бы некоторые кадры, я попытался перегрузить кнопку ОС, чтобы отразить поведение кнопки saveChanges.
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent event) {
saveChanges();
dispose();
}
});
Я оченьзапутался, как это может дать разные результаты.
E: Как предлагается, здесь приведен минимальный пример, который может воспроизвести это поведение:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
public class LeakingFrame extends JFrame{
public static LeakingFrame instance;
private ArrayList<String> content;
public static void main(String[] args) {
JFrame testFrame = new JFrame("test");
testFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton startButton = new JButton("Open LeakingFrame");
startButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
LeakingFrame.showInstance();
}
});
testFrame.add(startButton);
testFrame.pack();
testFrame.setVisible(true);
}
public static LeakingFrame showInstance(){
if(instance!=null){
instance.dispose();
instance = null;
System.gc();
}
instance = new LeakingFrame();
return instance;
}
private LeakingFrame(){
super("LeakingFrame");
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent event) {
dispose();
}
});
content = new ArrayList<String>();
for(int i=0;i<150000;i++){
content.add("LARGESTRING"); //So it takes up at least some amount of memory
}
JButton dispose = new JButton("Dispose");
dispose.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
dispose();
}
});
add(dispose);
pack();
setVisible(true);
}
}