Утечка памяти в Java, неизвестный поток, удерживающий удаленный JFrame - PullRequest
0 голосов
/ 24 мая 2018

Я провел последние 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);
}
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...