Em meio aos estudos que estou fazendo percebi algo sobre a coleta de lixo de componentes swing em java. Andei dando uma pesquisada na web e aqui no fórum, mas não achei nenhuma explicação que eu acredito que seja bem esclarecida.
Estou utilizando o profiler do netbeans para acompanhar a vida e morte dos objetos em um sistema que estou desenvolvendo.
Achei interresante que quando mando passar o GC pelo profiler, o GC coleta os objetos normalmente, mas quando se trata de componentes swing ele não coleta.
Fiz um teste aqui e pude notar que mesmo que eu abra um JInternalFrame sem nada e nenhuma referência, quando fecho o JInternalFrame e mando passar o GC o objeto continua a existir na memória.
Gostaria de entender melhor porque isso acontece e decidir vir aqui ao fórum debater essa questão com vcs. Por que quando se trata de componentes swing existe essa dificuldade do GC em coletar? Pude fazer vários testes aqui e nada de coletar o objeto da memória. Cheguei a abrir 30 vezes o mesmo JInternalFrame e mesmo passando o GC os 30 objetos continuaram alocados na memória, porém os outros objetos de classes que não envolvem componentes swing são coletados normalmente.
Por que tem alguém referenciando esses objetos.
No profiler do netbeans você pode descobrir quem. Basta ir até uma das referências e pedir para ele localizar o “root”.
Certifique-se também de fechar as janelas com dispose(), não com setVisible(false).
Hum. tá legal, vou ver se consigo localizar as referencias desses objetos por aqui. Mas tem algo que achei interessante também.
Todos os Listeners que são acionados a partir de qualquer objeto ficam residindo em memória como outros objetos também,
Exemplo:
Tenho uma classe ImportaArquivo com um botão com um actionListener -> e o método actionPerformed, o profiler mostra outro objeto dessa chamada como ImportaArquivo$1.
Hum. To entendo melhor. Então se Listeners tbm são objetos, isso quer dizer que o GC só irá coletálo caso o objeto pai seja coletado?
Vini, onde acho essa opção de localizar o “root”, aqui só têm “Realizar captura e mostar controle de pilhas de alocação” ele mostra uma arvore de quem chamou quem. É isso?
Sobre esse objeto JIFrame tenho o seguinte método:
public void abreRad(){
ImportaRadViewNovo importaRad = new ImportaRadViewNovo();
getDesktop().add(importaRad);
metodos.centralizaJInternalFrame(desktop, importaRad);
importaRad.setVisible(true);
}
Fora isso não tenho mais nada que referencie o IFrame. Esse método está dentro de um JFrame com um JMenu.
Não entendo como essa chamada segura a referencia do JIFrame. Outra coisa, ainda não tentei remover o IFrame do JDesktopPane. Vou fazer isso e postar aqui o que deu.
Acionei o evento formInternalFrameClosing() e dei um InicioView.getDesktop().removeAll(); e passei o GC e nada do objeto sair da memória.
Vini, o rastreamento de pilha me mostra quem chamou aquele objeto, mas quem chamou é sempre quem prende a referencia com o objeto nesse caso?
Fiz uns testes aqui e acabei verificando que é o desktopPane que tá referenciando ao IFrame, mas quando fecho o IFrame invoco o evento InternalFrameClosing() e dou um InicioView.getDesktop().remove(this); mas ele não está removendo a referência porque o IFrame não está sendo coletado. Quando não add o IFrame ao jDesktopPane ele cria o objeto e coleta normalmente, mas se eu add ao jDesktopPane, mesmo dando um remove como falado acima ele não coleta o objeto.
Como posso retirar essa referência de um IFrame de um JDesktopPane?
Opa, na verdade ele realmente retira o IFrame do desktopPane até mesmo sem o desktop.removeAll();
pego desktop e conto quantos frames ele têm e o resultado é 0 depois que fecho o IFrame, mas mesmo assim o objeto continua a existir na memória. Lí um bug no site da Sun sobre isso.
Acho que também acontece aqui ou acontecia não lembro, se não me engano tinha um comportamento estranho que se você minimiza-se o programa ele coletava os frames se não minimiza-se ele deixava na memória…
Faz tempo que não uso o JInternalFrame. Ainda assim, quando usava, havia um map que não deixava que fosse criada mais de uma instância e isso nunca se tornou um problema real de memória.
Mas talvez haja esse bug sim, o JInternalFrame é famoso por ter muitos bugs, e comportamentos inconsistentes com os SOs padrão.
É verdade vini, estive lendo mais sobre o JInternalFrame e realmente ví que várias pessoas já relataram comportamentos estranhos. Também utilizo um map para não deixar criar mais de uma instância, fiz isso também já que o GC não tava coletando ele.
Mas, que componentes vc usa? já que não utiliza os JIFrame’s. Existem outros componentes FREE que dão essa possibilidade de desenvolver em modelo MDI também?
Sim, eu retiro o IFrame do map. Estou tentando entender firmemente o q está acontecendo e estou a fazer vários testes aqui.
O problema não está nas referencias que fiz e sim em um componente de calendário que utilizo (JCalendar 1.3.3). Se eu adionar qualquer referencia do JDateChooser ao meu IFrame ele não deixa o IFrame ser coletado, retirei o JCalendar e testei com o JXDatePicker do SwingX e para minha surpresa, o IFrame foi coletado! O que realmente é estranho é que se eu abrir 10 IFrames e fechá-los logo em seguida 9 são coletados e 1 objeto continua a residir na memória, ele só será coletado quando outra instância do IFrame em questão, ou de um outro IFrame qualquer for feita! Que comportamento estranho. Vou dar uma lida nos fontes do JCalendar e ver se consigo achar o problema e resolvê-lo, enquanto isso não aconte vou utilizar o componente do SwingX mesmo para datas.
Vini, você falou que trabalha com frames mesmo ou tabbedPane, eu iria fazer com TabbedPane caso não conseguisse resolver esse problema com o IFrame, mas no caso dos Frames, como vc faz para retirar as abas que são abertas na barra de tarefas a cada Frame? ou vc na verdade não tira? Tentei trabalhar com Frames logo quando comecei com java há 1 ano + ou - mas toda vez que abro um novo Frame ele abre uma aba na barra de tarefas do SO. O JDialog não me entusiasmou muito porque ele simplesmente toma o sistema pra ele.
Não tiro. Até para o usuário poder dar ALT+TAB numa das janelas da aplicação.
Ele não põe abas para Dialogs, mesmo no caso das não modais.
O problema então está no JDateChooser? Talvez fosse uma boa você abrir o código dele e ver se não tem nenhuma variável estática por lá mantendo o componente vivo. Se tiver, ele provavelmente manterá o frame onde ele se encontra, pois ele tem referência ao owner.
Sim, o problema estava no JDateChooser, agora problema resolvido, no caso estou usando o JXDatePicker. Pra vc ter idéia, estou utilizando um progressBar que vc mesmo colocou aqui no fórum para medir o consumo de memória do sistema. Tive uma surpresa boa, minha aplicação inicia com uns 7 Mb, abrí uns 400 IFrame’s e aplicação foi para 120Mb aproximadamente, depois que fechei todos e chamei o GC, surpresa! a aplicação voltou para os mesmos 7Mb iniciais.
Agora vou tirar um tempo para dar uma olhada no JCalendar e procurar onde está o problema! por enquanto vou mantendo o JXDatePicker do SwingX.
Imagino quantas pessoas devem ter tido esse problema com o JCalendar e desistiram dos IFrame’s :shock:
Daqui pra frente vou prestar bastante atenção nos “componentes extras” que uso.
Também há outra coisa, estou utilizando o substance para mudar o look and feel do sistema e o JCalendar fica bem esquisitão com ele. Vou mexer nisso também.