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
Sempre que tem situações desse tipo, é melhor fazer um profiling. Senão fica difícil mesmo saber onde está o erro.
vitimnunes
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?
ViniGodoy
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á.
vitimnunes
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
J
juliocbq
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
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.
vitimnunes
juliocbq:
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
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.
eu estou fazendo assim…
// esta classe extende JScrollPaneTweetScrollPanescrollPane=newTweetScrollPane().createScrolledStatusList(rl);// eu estou recuperando o JPanel por um método da classe que implementa um JFramemainWindow.getHomePanel().removeAll();mainWindow.getHomePanel().setLayout(newBorderLayout());mainWindow.getHomePanel().add(scrollPane,BorderLayout.CENTER);mainWindow.validate();mainWindow.repaint();
não é assim que faz?
J
juliocbq
vitimnunes:
juliocbq:
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
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.
eu estou fazendo assim…
// esta classe extende JScrollPaneTweetScrollPanescrollPane=newTweetScrollPane().createScrolledStatusList(rl);// eu estou recuperando o JPanel por um método da classe que implementa um JFramemainWindow.getHomePanel().removeAll();mainWindow.getHomePanel().setLayout(newBorderLayout());mainWindow.getHomePanel().add(scrollPane,BorderLayout.CENTER);mainWindow.validate();mainWindow.repaint();
não é assim que faz?
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?
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.
J
juliocbq
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.
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.
vitimnunes
bom, vou apelar… rsrsrs
publicTweetScrollPanecreateScrolledStatusList(ResponseList<Status>rl)throwsIOException,TwitterException{MainWindowmainWindow=MainWindow.getInstance();BoxverticalBox=newBox(BoxLayout.Y_AXIS);for(Statusstatus:rl){LogFactory.write(LogFactory.DEBUG_LEVEL,"adicionando Tweet à lista");JPaneltweetPanel=newJPanel();JLabelimageLabel=newJLabel();JLabelscreenNameLabel=newJLabel();TweetTextPanetextLabel=newTweetTextPane();TweetTextPaneaboutLabel=newTweetTextPane();JLabelfavoriteLabel=newJLabel();JLabeldeleteLabel=newJLabel();JLabelretweetLabel=newJLabel();JLabelreplyLabel=newJLabel();StatusreferedStatus;if(status.isRetweet()){referedStatus=status.getRetweetedStatus();}elsereferedStatus=status;JavaTweetStatusjavaTweetStatus=newJavaTweetStatus(referedStatus);JavaTweetUserjavaTweetUser=newJavaTweetUser(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(newFont("Tahoma",1,12));screenNameLabel.setText(referedStatus.getUser().getScreenName());screenNameLabel.addMouseListener(newURLOpenerMouseListener("http://twitter.com/"+referedStatus.getUser().getScreenName()));textLabel.setText(javaTweetStatus.getText());StringaboutContentLabel=newString();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());}elseaboutContentLabel=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=newTweetTextPane(aboutContentLabel);favoriteLabel.addMouseListener(newFavoriteLabelMouseListener(status,favoriteLabel));favoriteLabel.setIcon(newImageIcon(getClass().getResource("/com/javatweet/resource/images/emblem-favorite.png")));favoriteLabel.setToolTipText("favorite this tweet");if(status.isFavorited()){favoriteLabel.setEnabled(true);}elsefavoriteLabel.setEnabled(false);if(status.getUser().getId()==mainWindow.getUserId()){deleteLabel.addMouseListener(newDeleteLabelMouseListener(status.getId()));deleteLabel.setIcon(newImageIcon(getClass().getResource("/com/javatweet/resource/images/user-trash.png")));deleteLabel.setToolTipText("delete");}retweetLabel.addMouseListener(newRetweetLabelMouseListener(status.getId()));retweetLabel.setIcon(newImageIcon(getClass().getResource("/com/javatweet/resource/images/mail-forward.png")));retweetLabel.setToolTipText("Retweet this Tweet");replyLabel.addMouseListener(newReplyLabelMouseListener(status.getId(),status.getInReplyToScreenName(),ReplyLabelMouseListener.REPLY_STATUS));replyLabel.setIcon(newImageIcon(getClass().getResource("/com/javatweet/resource/images/mail-reply-sender.png")));replyLabel.setToolTipText("Reply this Tweet");GroupLayouttweetPanelLayout=newGroupLayout(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");}returnnewTweetScrollPane(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
ViniGodoy
E quando você retira um tweetpanel de dentro do verticalbox?
vitimnunes
dai eu nao consigo criar, pois como vou gerar a lista sem o box pra organizar verticalmente?
J
juliocbq
dai eu nao consigo criar, pois como vou gerar a lista sem o box pra organizar verticalmente?
Presta atenção no que o V. Godoy falou. Se você ficar dando new nesses recursos sem liberá-los vai estourar o heap mesmo.
vitimnunes
dai eu nao consigo criar, pois como vou gerar a lista sem o box pra organizar verticalmente?
Presta atenção no que o V. Godoy falou. Se você ficar dando new nesses recursos sem liberá-los vai estourar o heap mesmo.
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?
vitimnunes
ninguém???
vitimnunes
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?