#java #swing #memory-leaks
#java #swing #утечки памяти
Вопрос:
Приложение Swing (GUI), пункт назначения — информационный терминал назначения. Профилировщик VirtualVM показывает, что утечка происходит из-за
java.awt.image.DataBufferInt
и
sun.awt.image.ImageRepresentation.setPixels
увеличение объема памяти происходит во время переходов между формами.
Логика приложения заключается в том, что существует несколько форм (JFrame — JF1, JF2 … JF7). Базовая форма JF1, нажатие кнопок JB открывает другие формы и закрывает себя и т. Д. Кроме JF1, все остальные формы имеют кнопки <> . В формах много JButtons с картинками, используется FancyButton:
public class FancyButton extends JButton {
private static final long serialVersionUID = 1L;
public FancyButton(Icon icon, Icon pressed) {
super(icon);
setFocusPainted(false);
//setRolloverEnabled(treue);
//setRolloverIcon(rollover);
setPressedIcon(pressed);
setBorderPainted(false);
setContentAreaFilled(false);
}
}
JButtons в формах отображаются следующим образом:
public class JF1 extends JFrame {
private static final GridBagConstraints gbc;
public static Timer tmr;
public JF1() throws Exception{
setUndecorated(true);
GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(this);
setAlwaysOnTop(true);
setLayout(new GridBagLayout());
setTitle("JF1");
}
public void init() throws Exception{
GlobalVars.jf2 = null;
GlobalVars.jf3 = null;
GlobalVars.jf4 = null;
GlobalVars.jf5 = null;
GlobalVars.jf6 = null;
GlobalVars.jf7 = null;
JXPanel contentPane = new JXPanel();
try {
ImagePainter ip = new ImagePainter(ImageIO.read(new File("skins/bg.jpg")));
ip.setFillHorizontal(true);
ip.setFillVertical(true);
contentPane.setBackgroundPainter(ip);
} catch (Exception e) {
e.printStackTrace();
}
Panel p01 = new Panel();
GridLayout gl01 = new GridLayout(1, 8, 2, 2);
p01.setLayout(gl01);
p01.setLocation(200, 300);
ResultSet rs = GlobalVars.st.executeQuery("select * from last_use_service order by nomer");
Icon i1;
Icon i2;
while (rs.next()){
final int l = rs.getInt(2);
i1 = new ImageIcon("skins/oper_logos/" l ".png");
i2 = new ImageIcon("skins/oper_logos/" l "_off.png");
FancyButton jbt = new FancyButton(i1,i2 );
jbt.setBounds(10, 100, 100, 100);
jbt.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
tmr.stop();
if(GlobalVars.jf3==null)
GlobalVars.jf3 = new JF3();
GlobalVars.jf3.init();
GlobalVars.jf3.setVisible(true); // Так открывается новая форма
setVisible(false); // и закрывается текущая
dispose();
}
});
p01.add(jbt);
}
rs.close();
addComponent(panel, p01, 0, 1, 2, 1, GridBagConstraints.WEST,GridBagConstraints.NORTHWEST);
...
Основной класс, который запускается первым:
public class Main {
public static class GlobalVars{
public static String TypeDB = "MySQL";
public static Connection DataBase;
public static Statement st;
public static JF1 jf1; // JFrame
public static JF2 jf2; // JFrame
public static JF3 jf3; // JFrame
...
}
public static void main(String[] args) throws Exception {
if(GlobalVars.TypeDB.equals("MySQL")){
Class.forName("com.mysql.jdbc.Driver");
GlobalVars.DataBase = DriverManager.getConnection("jdbc:mysql://localhost:3306/terminal?lc_ctype=UTF8", "root","123");
if(GlobalVars.jf1==null)
GlobalVars.jf1 = new JF1();
GlobalVars.jf1.init();
GlobalVars.jf1.setVisible(true);
}
...
}
Тем не менее, в методе Init Forms есть таймер, который через некоторое время открывает основную форму и закрывает текущую:
...
tmr = new Timer( s * 1000, updateCursorAction);
tmr.start();
...
private Action updateCursorAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
if(GlobalVars.jf1==null){
try {
GlobalVars.jf1= new JF1();
} catch (Exception e1) {
e1.printStackTrace();
}
}
tmr.stop();
try {
GlobalVars.jf1.init();
} catch (Exception e1) {
e1.printStackTrace();
}
GlobalVars.jf1.setVisible(true);
GlobalVars.jf2 = null;
setVisible(false);
dispose();
}
};
ДАМП КУЧИ
Пожалуйста, помогите исправить утечку памяти.
Я изменил всю панель на JPanel, и это код JF1:
package PlatService;
import java.awt.*;
public class JF1 extends JFrame {
private static final long serialVersionUID = 1L;
//private static final Insets insets = new Insets(0, 0, 0, 0);
private static String[] arrLang = { "rus", "eng", "taj" };
private static final GridBagConstraints gbc;
public static Timer tmr;
static {
public JF1() throws Exception{
setUndecorated(true);
GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(this);
setAlwaysOnTop(true);
setLayout(new GridBagLayout());
setTitle("JF1");
}
public void init() throws Exception{
GlobalVars.jf2 = null;
GlobalVars.jf3 = null;
GlobalVars.jf4 = null;
GlobalVars.jf5 = null;
GlobalVars.jf6 = null;
GlobalVars.jf7 = null;
JXPanel contentPane = new JXPanel();
try {
ImagePainter ip = new ImagePainter(ImageIO.read(new File("skins/bg.jpg")));
ip.setFillHorizontal(true);
ip.setFillVertical(true);
contentPane.setBackgroundPainter(ip);
} catch (Exception e) {
e.printStackTrace();
}
//setContentPane(contentPane);
JPanel panel = new JPanel();
panel.setLayout(new GridBagLayout());
addComponent(this, panel, 0, 0, 1, 1, GridBagConstraints.CENTER ,GridBagConstraints.BOTH);
JPanel p0 = new JPanel();
GridLayout gl0 = new GridLayout(1, 1, 1, 1);
final JLabel jl = new JLabel(new ImageIcon("skins/logo.png"));
p0.setLayout(gl0);
p0.add(jl);
addComponent(panel, p0, 0, 0, 2, 1, GridBagConstraints.NORTH ,GridBagConstraints.NORTH);
JPanel p01 = new JPanel();
GridLayout gl01 = new GridLayout(1, 8, 2, 2);
p01.setLayout(gl01);
p01.setLocation(200, 300);
ResultSet rs = GlobalVars.st.executeQuery("select * from last_use_service order by nomer");
Icon i1;
Icon i2;
while (rs.next()){
final int l = rs.getInt(2);
i1 = new ImageIcon("skins/oper_logos/" l ".png");
i2 = new ImageIcon("skins/oper_logos/" l "_off.png");
FancyButton jbt = new FancyButton(i1,i2 );
jbt.setBounds(10, 100, 100, 100);
jbt.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
tmr.stop();
GlobalVars.OperId = l;
GlobalVars.getCashCode=false;
if(GlobalVars.jf3==null)GlobalVars.jf3 = new JF3();
GlobalVars.jf3.init(); // = new JF3();
GlobalVars.jf3.setVisible(true);
setVisible(false);
dispose();
}
});
p01.add(jbt);
}
rs.close();
addComponent(panel, p01, 0, 1, 2, 1, GridBagConstraints.WEST,GridBagConstraints.NORTHWEST);
if (GlobalVars.LangId < 0 || GlobalVars.LangId > 2)GlobalVars.LangId = 0;
String sql = "SELECT * FROM OpGroup WHERE parent=0 order by enable desc, order_n";
PreparedStatement psmnt = GlobalVars.DataBase.prepareStatement(sql);
ResultSet rs2 = psmnt.executeQuery();
//rs = GlobalVars.st.executeQuery();
JPanel p = new JPanel();
GridLayout gl = new GridLayout(0, 2, 2, 2);
p.setLayout(gl);
p.setSize(300, 400);
p.setLocation(200, 300);
while (rs2.next()){
final int l = rs2.getInt(2);
i1 = new ImageIcon("skins/" arrLang[GlobalVars.LangId] "/services/" l ".png");
i2 = new ImageIcon("skins/" arrLang[GlobalVars.LangId] "/services/" l "_off.png");
FancyButton jbt = new FancyButton(i1,i2);
jbt.setBounds(10, 100, 100, 100);
if(rs2.getInt("enable")==1){
jbt.setEnabled(true);
}else{
jbt.setEnabled(false);
}
jbt.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
GlobalVars.getCashCode=false;
try {
tmr.stop();
ActionPerformed(event, GlobalVars.LangId, l);
} catch (Exception e) {
e.printStackTrace();
}
}
});
p.add(jbt);
}
addComponent(panel, p, 0, 2, 1, 1, GridBagConstraints.NORTH,GridBagConstraints.NORTH);
rs2.close();
JPanel p1 = new JPanel();
GridLayout gl1 = new GridLayout(5, 1, 5, 5);
// setLayout(new GridLayout(3, 4, 2, 2));
p1.setLayout(gl1);
// p2.setSize(300, 400);
// p2.setLocation(200, 300);
for (int i = 0; i < arrLang.length; i ) {
final int l = i;
i1 = new ImageIcon("skins/button_" arrLang[i] ".png");
i2 = new ImageIcon("skins/button_" arrLang[i] "_off.png");
FancyButton jbt = new FancyButton(i1, i2);
jbt.setBounds(10, 100, 100, 100);
//if (i == GlobalVars.LangId) {jbt.setEnabled(false);}
jbt.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
//Play.stop();
GlobalVars.LangId = l;
GlobalVars.getCashCode=false;
GlobalVars.jf1 = null;
try {
GlobalVars.jf1 = new JF1();
} catch (Exception e1) {
e1.printStackTrace();
}
try {
GlobalVars.jf1.init();
} catch (Exception e) {
e.printStackTrace();
}
tmr.stop();
GlobalVars.jf1.setVisible(true);
setVisible(false);
}
});
p1.add(jbt);
}
i1 = new ImageIcon("skins/" arrLang[GlobalVars.LangId] "/services/button_help.png");
i2 = new ImageIcon("skins/" arrLang[GlobalVars.LangId] "/services/button_help_off.png");
FancyButton jbt_help = new FancyButton(i1,i2);
jbt_help.setBounds(10, 100, 100, 100);
jbt_help.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
tmr.stop();
GlobalVars.getCashCode=false;
GlobalVars.jf1 = null;
try {
GlobalVars.jf1 = new JF1();
} catch (Exception e1) {
e1.printStackTrace();
}
try {
GlobalVars.jf1.init();
} catch (Exception e) {
e.printStackTrace();
}
GlobalVars.jf1.setVisible(true);
setVisible(false);
}
});
p1.add(jbt_help);
i1 = new ImageIcon("skins/" arrLang[GlobalVars.LangId] "/services/button_about.png");
i2 = new ImageIcon("skins/" arrLang[GlobalVars.LangId] "/services/button_about_off.png");
FancyButton jbt_about = new FancyButton(i1,i2);
jbt_about.setBounds(10, 100, 100, 100);
p1.add(jbt_about);
addComponent(panel, p1, 1, 2, 1, 1, GridBagConstraints.EAST,GridBagConstraints.EAST);
JPanel p011 = new JPanel();
GridLayout gl011 = new GridLayout( 1, 1, 1, 1);
gl011.setVgap(1);
JLabel jl12 = new JLabel("<html><hr></html>", JLabel.LEFT);
jl12.setAlignmentX(TOP_ALIGNMENT);
jl12.setBackground(Color.red);
p011.setLayout(gl011);
p011.add(jl12);
p011.setSize(10, 90);
addComponent(panel, p011, 0, 3, 4, 1, GridBagConstraints.WEST, GridBagConstraints.NORTH);
JPanel p0112 = new JPanel();
GridLayout gl0112 = new GridLayout( 1, 1, 1, 1);
gl0112.setVgap(1);
JLabel jl122 = new JLabel("<html><hr><H2>" GlobalVars.StatusMessage[GlobalVars.LangId] "</H2></html>", JLabel.CENTER);
jl122.setAlignmentX(TOP_ALIGNMENT);
p0112.setLayout(gl0112);
p0112.add(jl122);
p0112.setSize(10, 90);
addComponent(this, p0112, 0, 5, 5, 1, GridBagConstraints.SOUTH, GridBagConstraints.BOTH);
if(!GlobalVars.stTerminal.equals("301")){
GlobalVars.stTerminal = "301";
String sql1 = "INSERT INTO perif_status (perif,state,date_change) values('terminal','" GlobalVars.stTerminal "',UNIX_TIMESTAMP())";
//System.out.println(sql1);
try {
GlobalVars.st.execute(sql1);
} catch (SQLException e) {
e.printStackTrace();
}
}
GlobalVars.NomerAb="";
GlobalVars.GroupId = 0;
GlobalVars.getCashCode=true;
tmr = new Timer(1000, updateCursorAction);
tmr.start();
System.gc();
}
public void update(){
private Action updateCursorAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
if(GlobalVars.doBlock){
if(!GlobalVars.stTerminal.equals("303")){
GlobalVars.stTerminal = "303";
String sql1 = "INSERT INTO perif_status (perif,state,date_change) values('terminal','" GlobalVars.stTerminal "',UNIX_TIMESTAMP())";
String sql2 = "UPDATE settings set value='" GlobalVars.stTerminal "' WHERE variable='terminal_state'";
System.out.println(sql1);
System.out.println(sql2);
try {
GlobalVars.st.execute(sql1);
} catch (SQLException e1) {
e1.printStackTrace();
}
try {
GlobalVars.st.execute(sql2);
} catch (SQLException e1) {
e1.printStackTrace();
}
}
String sql1 = "UPDATE commands SET status=1, date_execute=UNIX_TIMESTAMP() WHERE id_on_server=" GlobalVars.doCommandId;
System.out.println(sql1);
try {
GlobalVars.st.execute(sql1);
} catch (SQLException e1) {
e1.printStackTrace();
}
if(GlobalVars.jf7==null)
try {
GlobalVars.jf7= new JF7();
} catch (Exception e1) {
e1.printStackTrace();
}
try {
GlobalVars.jf7.init();
} catch (Exception e1) {
e1.printStackTrace();
}
GlobalVars.doBlock=false;
GlobalVars.doCommandId = 0;
GlobalVars.jf7.setVisible(true);
GlobalVars.jf1 = null;
setVisible(false);
tmr.stop();
setVisible(false);
dispose();
}
}
};
private static void addComponent(Container container, Component component,int gridx, int gridy, int gridwidth, int gridheight, int anchor,int fill) {
Insets ins = new Insets(0, 0, 0, 0);
GridBagConstraints gbc1 = new GridBagConstraints(gridx, gridy,gridwidth, gridheight, 1.0, 1.0, anchor, fill, ins, 0, 0);
container.add(component, gbc1);
}
public void ActionPerformed(ActionEvent event, int lID,int gId) throws Exception {
GlobalVars.GroupId = gId;
if(GlobalVars.jf2==null)GlobalVars.jf2 = new JF2();
GlobalVars.jf2.init();
GlobalVars.jf2.setVisible(true);
setVisible(false);
dispose();
}
}
И это новый дамп
Комментарии:
1. Не могли бы вы объяснить, как вы пришли к выводу, что это утечка? Это только потому, что память растет? Потому что это может означать, что сборщик мусора еще не запущен. Если вы не предоставите более значимые показатели (и нет, я не загружаю rar-файл с rapidshare), на самом деле нет никакого способа помочь.
2. Из-за OutOfMemoryError. И я отслеживаю это с помощью VirtualVM
Ответ №1:
У вас действительно есть утечка, но простой просмотр количества загруженных классов не поможет вам выяснить, что это такое.
Если вы загрузите свой снимок в JProfiler (отказ от ответственности: моя компания разрабатывает JProfiler) и посмотрите на представление самых больших объектов, вы увидите, что используемая память обусловлена двойной буферизацией нескольких JF1
кадров и экземпляров панели, которые принадлежат этим кадрам.
Ваша проблема в том, что JF1
фреймы скрыты, но не удалены. Поиск корней GC показывает, что содержатся все 3 невидимых фрейма, в java.awt.Window.allWindows
которых не может dispose()
быть вызван регистр if. Вы вызываете много кода вне потока отправки событий. Например, вы не должны вызывать setVisible() из потока таймера. Попробуйте распечатать создание фреймов JF1 и вызов их методов dispose и проверьте, где они не совпадают.
Комментарии:
1. That is where I dispose JF1 if(GlobalVars.jf3==null) GlobalVars.jf3 = new JF3(); GlobalVars.jf3.init(); GlobalVars.jf3.setVisible(true); // Так открывается новая форма setVisible(false); // и закрывается текущая dispose();
2. Тогда эти строки не вызываются. Попробуйте распечатать создание JF1 и их удаление и проверьте, совпадают ли они.
3. Я изменил свой ответ, чтобы отразить ваш новый дамп кучи. Ваши фреймы определенно не удалены.
4. Большое вам спасибо! Я это исправил.
Ответ №2:
вы создаете множество контейнеров верхнего уровня с J-компонентами или Images
внутри них, и они Objects
в этой форме никогда не JVM Used_Memory
использовались, вам нужно очистить содержимое неиспользуемых Top-level Containers
,
лучше было бы
создайте JFrame
только один раз, а для другого всплывающего окна создайте только одно JDialog/JWindow
помещенное здесь JPanel
, повторно используйте этот контейнер, удалив JComponents
из JPanel
,
JPanel.removeAll();
DefaultCloseOperation будет
JDialog#setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE)
или вы можете установить
JDialog#setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE)
и тогда вы можете вызывать только
JDialog#setVisible(false / true)
Редактировать
и вы должны закрыть все JDBC ResultSet, Statement, PreparedStatement в блоке finally, потому что эти объекты никогда не исчезали из JVM UsedMemory тоже