Pessoal, estou fazendo uma aplicação em Java com interface Swing no Netbeans. Porém, ao analizar a execução do programa reparei que o consumo de memória do processo javaw.exe (windows) java -jar (linux) estava crescendo indefinidamente. A aplicação consiste em um cliente do Twitter usando api twitter4j.
Estranhei quando vi que minha aplicação estava consumindo mais memória do que o firefox, ela inicia usando mais ou menos 40.000k de consumo de memória, mas em questão de minutos chega a consumir 120.000k a 130.000k de memória.
Não tinha percebido antes pois tenho uma quantidade considerável de memória no meu pc, mas quando fui testar no linux usando virtualbox com uma qtde menor de memória reparei o que estava acontecendo. Testei no windows e vi que acontecia o mesmo.
O que pode estar causando isso? Será que o fato de estar atualizando a gui constantemente a cada (1 a 2 min)?
Estou meio sem saber como sair dessa
Use o Visual VM (que vem com o JDK) para descobrir onde está o memory leak?
https://visualvm.dev.java.net/
Sempre que tem situações desse tipo, é melhor fazer um profiling. Senão fica difícil mesmo saber onde está o erro.
eu usei o visualvm para olhar o heap dump e ver o que estava acontecedo… não sou muito bom em analizar isso, fiquei meio confuso mas reparei nos maiores objetos por tamanho retido e era um JPanel.
O que acontece é mais ou menos o seguinte, eu recupero uma lista de mensagens (tweets) e crio um JPanel para cada uma, agrupo todos os JPanels em um JScrollPane e retorno como resultado de um método.
Eu insiro o JScrollPane criado pelo metodo em um JPanel de um JFrame, mas antes de inserir eu faço panel.removeAll() para que ele não concatene os resultados e sim recrie o conteúdo do panel, depois panel.validate(); panel.reprint();
Eu pensei que assim, ele ia sempre liberar da memória o conteúdo anterior do JPanel e inserir o novo, mas parece que ele não faz isso e o consumo de memória cresce de maneira descontrolada.
Há algo de errado nisto que estou fazendo?
Se o JPanel não está sendo liberado, é pq ele está sendo referenciado por alguém. Isso é fato.
O profiler deixa você ver as instâncias dos vários JPanel no heap dump. Em cada instância, existe uma árvore indicando quem está referenciando aquele JPanel. Pode ser algum listener, ou alguma lista que você esqueceu de remover o painel. Mas o importante é que você remova todas as referências para que o painel seja coletado. Você pode abrir as instâncias e olhar algumas delas. Como geralmente a maioria vai ser mesmo dos JPanels com problema, uma rápida amostragem já te indicará onde o problema está.
não entendo, toda vez que faço o profile o resultado é diferente, vou mandar alguns screenshots…
não sei se ficou bem claro o que estou fazendo… o programa chama um método que retorna um JScrollPane preenchido com componentes e este resultado é adicionado em um JPanel de um JTabbedPane.
O programa atualiza periódicamente ( 1,5min ) através de um Timer. a intenção era que ele consumisse a mesma quantidade de memória, pois o número de componentes inseridos é exatamente o mesmo sempre.
Mas sempre que o programa atualiza a memória consumida aumenta. Se você quiser te mostro algumas partes do código ou te passo o projeto do netbeans pra você dar uma olhada
[quote=vitimnunes]não sei se ficou bem claro o que estou fazendo… o programa chama um método que retorna um JScrollPane preenchido com componentes e este resultado é adicionado em um JPanel de um JTabbedPane.
O programa atualiza periódicamente ( 1,5min ) através de um Timer. a intenção era que ele consumisse a mesma quantidade de memória, pois o número de componentes inseridos é exatamente o mesmo sempre.
Mas sempre que o programa atualiza a memória consumida aumenta. Se você quiser te mostro algumas partes do código ou te passo o projeto do netbeans pra você dar uma olhada[/quote]
se o programa atualiza e a memória aumenta, vai precisar rever seu JTabbedPane. Talvez você esteja adicionando paineis e não esteja removendo os que não tem mais utilidade. Isso deve estar gerando os leaks na suia aplicação.
[quote=juliocbq][quote=vitimnunes]não sei se ficou bem claro o que estou fazendo… o programa chama um método que retorna um JScrollPane preenchido com componentes e este resultado é adicionado em um JPanel de um JTabbedPane.
O programa atualiza periódicamente ( 1,5min ) através de um Timer. a intenção era que ele consumisse a mesma quantidade de memória, pois o número de componentes inseridos é exatamente o mesmo sempre.
Mas sempre que o programa atualiza a memória consumida aumenta. Se você quiser te mostro algumas partes do código ou te passo o projeto do netbeans pra você dar uma olhada[/quote]
se o programa atualiza e a memória aumenta, vai precisar rever seu JTabbedPane. Talvez você esteja adicionando paineis e não esteja removendo os que não tem mais utilidade. Isso deve estar gerando os leaks na suia aplicação.
[/quote]
eu estou fazendo assim…
// esta classe extende JScrollPane
TweetScrollPane scrollPane = new TweetScrollPane().createScrolledStatusList( rl );
// eu estou recuperando o JPanel por um método da classe que implementa um JFrame
mainWindow.getHomePanel().removeAll();
mainWindow.getHomePanel().setLayout( new BorderLayout() );
mainWindow.getHomePanel().add( scrollPane, BorderLayout.CENTER );
mainWindow.validate();
mainWindow.repaint();
não é assim que faz?
[quote=vitimnunes][quote=juliocbq][quote=vitimnunes]não sei se ficou bem claro o que estou fazendo… o programa chama um método que retorna um JScrollPane preenchido com componentes e este resultado é adicionado em um JPanel de um JTabbedPane.
O programa atualiza periódicamente ( 1,5min ) através de um Timer. a intenção era que ele consumisse a mesma quantidade de memória, pois o número de componentes inseridos é exatamente o mesmo sempre.
Mas sempre que o programa atualiza a memória consumida aumenta. Se você quiser te mostro algumas partes do código ou te passo o projeto do netbeans pra você dar uma olhada[/quote]
se o programa atualiza e a memória aumenta, vai precisar rever seu JTabbedPane. Talvez você esteja adicionando paineis e não esteja removendo os que não tem mais utilidade. Isso deve estar gerando os leaks na suia aplicação.
[/quote]
eu estou fazendo assim…
// esta classe extende JScrollPane
TweetScrollPane scrollPane = new TweetScrollPane().createScrolledStatusList( rl );
// eu estou recuperando o JPanel por um método da classe que implementa um JFrame
mainWindow.getHomePanel().removeAll();
mainWindow.getHomePanel().setLayout( new BorderLayout() );
mainWindow.getHomePanel().add( scrollPane, BorderLayout.CENTER );
mainWindow.validate();
mainWindow.repaint();
não é assim que faz?[/quote]
Tenta isolar o funcionamento do programa, e ver quando ele começa a gerar leaks. A melhor maneira de se fazer isso é separar os metodos e criar uma classe de testes.
Chame os métodos separadamente, e veja na jvisualvm quando o heap começa a crescer. Dessa maneira fica mais simples de detectar. Você cria algum socket, ou conexão com banco de dados que não está encerrando?
conexão com o banco de dados não tem. socket eu não implementei, só se a API do Twitter que estou utilizando faz isso.
fiz alguns testes parecidos com o que você falou. a verdade é que sem atualizar a GUI o consumo de memórica fica parado. mas toda vez que atualiza o consume cresce gradativamente.
como disse anteriormente não sou muito bom com visualvm, não sei mexer muito mas tenho tentado de um tudo pra corrigir este problema.
[quote=vitimnunes]conexão com o banco de dados não tem. socket eu não implementei, só se a API do Twitter que estou utilizando faz isso.
fiz alguns testes parecidos com o que você falou. a verdade é que sem atualizar a GUI o consumo de memórica fica parado. mas toda vez que atualiza o consume cresce gradativamente.
como disse anteriormente não sou muito bom com visualvm, não sei mexer muito mas tenho tentado de um tudo pra corrigir este problema.[/quote]
Foca no método que faz o refresh da interface da sua aplicação. Com certeza o leak está ae. Procure por itens que foram criados e não foram descartados. Na maioria das vezes é uma coisa boba, mas difícil de ser visualizada.
bom, vou apelar… rsrsrs
public TweetScrollPane createScrolledStatusList( ResponseList<Status> rl ) throws IOException, TwitterException {
MainWindow mainWindow = MainWindow.getInstance();
Box verticalBox = new Box( BoxLayout.Y_AXIS );
for( Status status : rl ) {
LogFactory.write( LogFactory.DEBUG_LEVEL, "adicionando Tweet à lista" );
JPanel tweetPanel = new JPanel();
JLabel imageLabel = new JLabel();
JLabel screenNameLabel = new JLabel();
TweetTextPane textLabel = new TweetTextPane();
TweetTextPane aboutLabel = new TweetTextPane();
JLabel favoriteLabel = new JLabel();
JLabel deleteLabel = new JLabel();
JLabel retweetLabel = new JLabel();
JLabel replyLabel = new JLabel();
Status referedStatus;
if( status.isRetweet() ) {
referedStatus = status.getRetweetedStatus();
} else referedStatus = status;
JavaTweetStatus javaTweetStatus = new JavaTweetStatus( referedStatus );
JavaTweetUser javaTweetUser = new JavaTweetUser( referedStatus.getUser() );
tweetPanel.setBorder(BorderFactory.createEtchedBorder());
imageLabel.setIcon( javaTweetUser.getProfileImage() );
imageLabel.setText( String.valueOf( referedStatus.getUser().getFollowersCount() ) );
imageLabel.setHorizontalTextPosition(SwingConstants.CENTER);
imageLabel.setVerticalTextPosition(SwingConstants.BOTTOM);
screenNameLabel.setFont(new Font("Tahoma", 1, 12));
screenNameLabel.setText( referedStatus.getUser().getScreenName() );
screenNameLabel.addMouseListener( new URLOpenerMouseListener( "http://twitter.com/" + referedStatus.getUser().getScreenName() ) );
textLabel.setText( javaTweetStatus.getText() );
String aboutContentLabel = new String();
if( status.getInReplyToStatusId() > 0 ) {
aboutContentLabel = String.format( "%s via %s <a href=\"http://twitter.com/%s/status/%d\">in reply to %s</a>",
javaTweetStatus.getCreatedAtAsHumanFriendly(),
referedStatus.getSource(), referedStatus.getInReplyToScreenName(),
referedStatus.getInReplyToStatusId(), referedStatus.getInReplyToScreenName() );
} else aboutContentLabel = String.format( "%s via %s", javaTweetStatus.getCreatedAtAsHumanFriendly(), status.getSource() );
if( status.isRetweet() ) {
aboutContentLabel = aboutContentLabel.concat( String.format( " Retweeted by <a href=\"http://twitter.com/%s\">%s</a>", status.getUser().getScreenName(), status.getUser().getScreenName() ) );
}
aboutLabel = new TweetTextPane( aboutContentLabel );
favoriteLabel.addMouseListener( new FavoriteLabelMouseListener( status, favoriteLabel ) );
favoriteLabel.setIcon(new ImageIcon(getClass().getResource("/com/javatweet/resource/images/emblem-favorite.png")));
favoriteLabel.setToolTipText("favorite this tweet");
if( status.isFavorited() ) {
favoriteLabel.setEnabled( true );
} else favoriteLabel.setEnabled( false );
if( status.getUser().getId() == mainWindow.getUserId() ) {
deleteLabel.addMouseListener( new DeleteLabelMouseListener( status.getId() ) );
deleteLabel.setIcon(new ImageIcon(getClass().getResource("/com/javatweet/resource/images/user-trash.png")));
deleteLabel.setToolTipText("delete");
}
retweetLabel.addMouseListener( new RetweetLabelMouseListener( status.getId() ) );
retweetLabel.setIcon(new ImageIcon(getClass().getResource("/com/javatweet/resource/images/mail-forward.png")));
retweetLabel.setToolTipText("Retweet this Tweet");
replyLabel.addMouseListener( new ReplyLabelMouseListener( status.getId(), status.getInReplyToScreenName(), ReplyLabelMouseListener.REPLY_STATUS ) );
replyLabel.setIcon(new ImageIcon(getClass().getResource("/com/javatweet/resource/images/mail-reply-sender.png")));
replyLabel.setToolTipText("Reply this Tweet");
GroupLayout tweetPanelLayout = new GroupLayout(tweetPanel);
tweetPanel.setLayout(tweetPanelLayout);
tweetPanelLayout.setHorizontalGroup(
tweetPanelLayout.createParallelGroup(Alignment.LEADING)
.addGroup(tweetPanelLayout.createSequentialGroup()
.addComponent(imageLabel)
.addPreferredGap(ComponentPlacement.RELATED)
.addGroup(tweetPanelLayout.createParallelGroup(Alignment.TRAILING)
.addGroup(tweetPanelLayout.createSequentialGroup()
.addComponent(screenNameLabel)
.addPreferredGap(ComponentPlacement.UNRELATED)
.addComponent(textLabel, GroupLayout.DEFAULT_SIZE, 629, Short.MAX_VALUE)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(favoriteLabel))
.addGroup(tweetPanelLayout.createSequentialGroup()
.addComponent(aboutLabel)
.addPreferredGap(ComponentPlacement.RELATED, 437, Short.MAX_VALUE)
.addComponent(replyLabel)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(retweetLabel)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(deleteLabel)))
.addContainerGap())
);
tweetPanelLayout.setVerticalGroup(
tweetPanelLayout.createParallelGroup(Alignment.LEADING)
.addComponent(imageLabel)
.addGroup(tweetPanelLayout.createSequentialGroup()
.addGroup(tweetPanelLayout.createParallelGroup(Alignment.BASELINE)
.addComponent(screenNameLabel)
.addComponent(textLabel)
.addComponent(favoriteLabel))
.addPreferredGap(ComponentPlacement.RELATED)
.addGroup(tweetPanelLayout.createParallelGroup(Alignment.BASELINE)
.addComponent(aboutLabel)
.addComponent(deleteLabel)
.addComponent(retweetLabel)
.addComponent(replyLabel)))
);
verticalBox.add( tweetPanel );
LogFactory.write( LogFactory.DEBUG_LEVEL, "Tweet adicionado com sucesso" );
}
return new TweetScrollPane( verticalBox );
}
desculpa pessoal, mas ja comentei e descomentei um monte de coisa um monte de vez… mas nada. alguem pode me dizer se vê algo???
este é o método que cria o JScrollPane que vou inserir no JPanel do meu JTabbedPane
[]'s
E quando você retira um tweetpanel de dentro do verticalbox?
dai eu nao consigo criar, pois como vou gerar a lista sem o box pra organizar verticalmente?
dai eu nao consigo criar, pois como vou gerar a lista sem o box pra organizar verticalmente?[/quote]
Presta atenção no que o V. Godoy falou. Se você ficar dando new nesses recursos sem liberá-los vai estourar o heap mesmo.
dai eu nao consigo criar, pois como vou gerar a lista sem o box pra organizar verticalmente?[/quote]
Presta atenção no que o V. Godoy falou. Se você ficar dando new nesses recursos sem liberá-los vai estourar o heap mesmo.[/quote]
desculpa minha ignorância mas, ainda não entendo, como vou conseguir criar uma lista se eu não gerars os panels para adicionar?
tentei colocar a criação dos objetos fora do loop mas não deu certo, além de add so o 1 ainda continua sem diminuir o consumo de memória (diminuiu a qtde inicial porem continua crescendo sem parar)
alguém pode dar um exemplo em código?
eu fiz um novo teste com jvisualvm e vi que ele está gerando duas instâncias do Box, ele não tá liberando o primeiro mesmo após o removeAll(), mas isso não ajuda muito já que o leak inda pode ser em qualquer lugar.
gostariam que eu compartilhasse o projeto pra ver o código?
depois de muita luta, achei esse tutorial ( em inglês, é claro ) de como descobrir leaks de memória usando jvisualvm (http://rejeev.blogspot.com/2009/04/analyzing-memory-leak-in-java.html)
ví que o uso de uma CSS no JEditorPane estava criando um uso de memória bem grande, mas ainda não consegui resolver o problema.
mas continuo na luta