Eu tenho lido muitas das perguntas novatas sobre Java finalize()
e acho meio desconcertante que ninguém tenha deixado claro que finalize () é uma maneira não confiável de limpar recursos. Vi alguém comentar que eles o usam para limpar o Connections, o que é realmente assustador, pois a única maneira de chegar perto da garantia de que o Connection está fechado é implementar finalmente o try (catch).
Eu não tinha formação em CS, mas tenho programado profissionalmente em Java há quase uma década e nunca vi ninguém implementando finalize()
em um sistema de produção. Isso ainda não significa que ele não tem seus usos ou que as pessoas com quem trabalhei estão fazendo certo.
Portanto, minha pergunta é: quais casos de uso existem para implementar finalize()
que não podem ser tratados de maneira mais confiável por meio de outro processo ou sintaxe no idioma?
Forneça cenários específicos ou sua experiência, simplesmente repetir um livro de texto Java ou finalizar o uso pretendido não é suficiente, pois não é o objetivo desta pergunta.
finalize()
. No entanto, o código da biblioteca da plataforma , comoSocketInputStream
, que gerencia recursos nativos em nome do responsável pela chamada, tenta minimizar o risco de vazamentos de recursos (ou usa mecanismos equivalentes, como osPhantomReference
que foram adicionados posteriormente). Portanto, o ecossistema precisa deles , mesmo que 99,9999% dos desenvolvedores nunca escrevam um.Respostas:
Você pode usá-lo como um pano de fundo para um objeto que contém um recurso externo (soquete, arquivo, etc.). Implemente um
close()
método e documento que precisa ser chamado.Implemente
finalize()
para executar oclose()
processamento se você detectar que isso não foi feito. Talvez com algo despejado parastderr
indicar que você está limpando depois de uma chamada de buggy.Ele fornece segurança extra em uma situação excepcional / de buggy. Nem todo chamador sempre faz as
try {} finally {}
coisas corretas . Infelizmente, mas é verdade na maioria dos ambientes.Concordo que raramente é necessário. E, como os comentaristas apontam, ele vem com a sobrecarga do GC. Use apenas se você precisar dessa segurança "cinto e suspensórios" em um aplicativo de longa duração.
Eu vejo que a partir do Java 9,
Object.finalize()
está obsoleta! Eles nos apontamjava.lang.ref.Cleaner
ejava.lang.ref.PhantomReference
como alternativas.fonte
finalize()
é uma dica para a JVM de que pode ser bom executar seu código em um momento não especificado. Isso é bom quando você deseja que o código falhe misteriosamente na execução.Fazer algo significativo nos finalizadores (basicamente qualquer coisa, exceto o log) também é bom em três situações:
Se você acha que precisa finalizar (), às vezes o que você realmente deseja é uma referência fantasma (que no exemplo dado pode conter uma referência rígida a uma conexão usada por seu referendo e fechá-la depois que a referência fantasma estiver na fila). Isso também tem a propriedade de que misteriosamente nunca pode ser executado, mas pelo menos não pode chamar métodos ou ressuscitar objetos finalizados. Portanto, é ideal para situações em que você não precisa absolutamente fechar essa conexão de maneira limpa, mas gostaria bastante, e os clientes da sua classe não podem ou não querem se fechar (o que é realmente justo o suficiente - o que' é o ponto de ter um coletor de lixo, se você criar interfaces que exijam uma ação específica antes da coleta? Isso apenas nos coloca de volta aos dias de malloc / free.)
Outras vezes, você precisa do recurso que acha que está gerenciando para ser mais robusto. Por exemplo, por que você precisa fechar essa conexão? No fim das contas, ele deve se basear em algum tipo de E / S fornecida pelo sistema (soquete, arquivo, qualquer que seja). Por que você não pode confiar no sistema para fechá-lo quando o nível mais baixo de recursos é determinado? Se o servidor na outra extremidade exigir absolutamente que você feche a conexão de maneira limpa, em vez de apenas soltar o soquete, o que acontecerá quando alguém tropeçar no cabo de alimentação da máquina em que seu código está sendo executado ou quando a rede intermediária for desativada?
Isenção de responsabilidade: trabalhei em uma implementação da JVM no passado. Eu odeio finalizadores.
fonte
finalize()
sem dar nenhuma razão para alguém realmente querer usá-lo.java.lang.ref.Reference
e suas subclasses. O Java 1 tinha variáveis :-) E sim, também tinha finalizadores.Uma regra simples: nunca use finalizadores.
Somente o fato de um objeto ter um finalizador (independentemente do código que ele executa) é suficiente para causar uma sobrecarga considerável na coleta de lixo.
De um artigo de Brian Goetz:
fonte
A única vez que usei finalizar no código de produção foi implementar uma verificação de que os recursos de um determinado objeto foram limpos e, se não, registrar uma mensagem muito vocal. Na verdade, ele não tentou fazer isso sozinho, apenas gritou muito se não fosse feito corretamente. Acabou por ser bastante útil.
fonte
Trabalho em Java profissionalmente desde 1998 e nunca o implementei
finalize()
. Nem uma vez.fonte
A resposta aceita é boa, eu só queria acrescentar que agora existe uma maneira de ter a funcionalidade de finalizar sem realmente usá-la.
Veja as classes "Referência". Referência fraca, Referência fantasma e Referência suave.
Você pode usá-los para manter uma referência a todos os seus objetos, mas essa referência SOZINHO não interromperá o GC. O mais interessante sobre isso é que você pode chamar um método quando ele será excluído, e pode-se garantir que esse método seja chamado.
Quanto à finalização: usei finalizar uma vez para entender quais objetos estavam sendo liberados. Você pode jogar alguns jogos legais com estática, contagem de referência e outras coisas - mas foi apenas para análise, mas cuidado com códigos como este (não apenas na finalização, mas é onde você provavelmente verá):
É um sinal de que alguém não sabia o que estava fazendo. "Limpar" assim nunca é praticamente necessário. Quando a classe é submetida ao GC, isso é feito automaticamente.
Se você encontrar um código como esse em uma finalização, é garantido que a pessoa que o escreveu estava confusa.
Se estiver em outro lugar, pode ser que o código seja um patch válido para um modelo ruim (uma classe permanece por um longo tempo e, por algum motivo, as coisas referenciadas precisavam ser liberadas manualmente antes que o objeto fosse submetido ao GC). Geralmente, é porque alguém se esqueceu de remover um ouvinte ou algo assim e não consegue entender por que o objeto não está sendo usado no GC, para apenas excluir as coisas a que se refere e encolher os ombros e ir embora.
Nunca deve ser usado para limpar as coisas "Mais rápido".
fonte
Não sei bem o que você pode fazer disso, mas ...
Então, acho que o Sol encontrou alguns casos em que (eles acham) que deveriam ser usados.
fonte
finalize
vez de realmente usá-lo?$FAVORITE_IDE
.====================================
resultado:
=======================================
Portanto, você pode tornar uma instância inacessível acessível no método finalize.
fonte
System.gc();
não é uma garantia de que o coletor de lixo realmente será executado. É critério total do GC limpar o heap (talvez ainda haja heap livre suficiente, talvez não muito fragmentado ...). Portanto, é apenas um humilde pedido do programador "por favor, você poderia me ouvir e sair correndo?" e não um comando "corra agora como eu digo!". Desculpe por usar palavras linguais, mas diz exatamente como o GC da JVM funciona.finalize()
pode ser útil para detectar vazamentos de recursos. Se o recurso precisar ser fechado, mas não for gravado, o fato de não ter sido fechado em um arquivo de log e fechá-lo. Dessa forma, você remove o vazamento de recursos e oferece a si mesmo uma maneira de saber que isso aconteceu para que você possa corrigi-lo.Estou programando em Java desde 1.0 alpha 3 (1995) e ainda tenho que substituir a finalização por qualquer coisa ...
fonte
Você não deve depender de finalize () para limpar seus recursos para você. finalize () não será executado até que a classe seja coletada como lixo, se for o caso. É muito melhor liberar explicitamente recursos quando você terminar de usá-los.
fonte
Para destacar um ponto nas respostas acima: os finalizadores serão executados no único thread do GC. Ouvi falar de uma grande demonstração da Sun, na qual os desenvolvedores dormiram um pouco em alguns finalizadores e intencionalmente trouxeram uma demo 3D extravagante de joelhos.
É melhor evitar, com possível exceção dos diagnósticos do ambiente de teste.
O pensamento de Eckel em Java tem uma boa seção sobre isso.
fonte
Hmmm, uma vez eu o usei para limpar objetos que não estavam sendo devolvidos a um pool existente.
Eles foram passados muito, por isso era impossível dizer quando eles poderiam ser devolvidos com segurança para a piscina. O problema era que ele introduziu uma penalidade enorme durante a coleta de lixo, muito maior do que qualquer economia com o agrupamento de objetos. Estava em produção por cerca de um mês antes de eu arrancar toda a piscina, tornar tudo dinâmico e acabar com ela.
fonte
Tenha cuidado com o que você faz em um
finalize()
. Especialmente se você o estiver usando para coisas como ligar para close () para garantir que os recursos sejam limpos. Tivemos várias situações em que tínhamos bibliotecas JNI vinculadas ao código java em execução e, em qualquer circunstância em que usamos finalize () para invocar métodos JNI, teríamos uma corrupção muito ruim da pilha de java. A corrupção não foi causada pelo próprio código JNI subjacente; todos os rastreamentos de memória estavam corretos nas bibliotecas nativas. Era justamente o fato de estarmos chamando os métodos JNI de finalize ().Isso ocorreu com o JDK 1.5, que ainda é amplamente utilizado.
Não descobrimos que algo deu errado até muito mais tarde, mas, no final, o culpado sempre foi o método finalize () usando chamadas JNI.
fonte
Ao escrever um código que será usado por outros desenvolvedores que exija que algum tipo de método de "limpeza" seja chamado para liberar recursos. Às vezes, esses outros desenvolvedores esquecem de chamar seu método de limpeza (ou fechamento, destruição ou o que seja). Para evitar possíveis vazamentos de recursos, você pode verificar o método finalize para garantir que o método foi chamado e, caso contrário, você mesmo pode chamá-lo.
Muitos drivers de banco de dados fazem isso em suas implementações de Declaração e Conexão para fornecer um pouco de segurança contra os desenvolvedores que se esquecem de encerrar a conversa.
fonte
Edit: Ok, realmente não funciona. Eu o implementei e pensei que, se às vezes falhar, tudo bem para mim, mas ele nem sequer chamou o método finalize uma única vez.
Não sou programador profissional, mas no meu programa tenho um caso que acho que é um exemplo de bom uso de finalize (), que é um cache que grava seu conteúdo no disco antes de ser destruído. Como não é necessário que seja executado todas as vezes na destruição, isso apenas acelera o meu programa, espero que não tenha feito errado.
fonte
Pode ser útil remover itens que foram adicionados a um local global / estático (por necessidade) e precisam ser removidos quando o objeto é removido. Por exemplo:
fonte
this
instância passada a ele no construtor, essa instância nunca se torna inacessível, portantofinalize()
, nunca será limpa de qualquer maneira. Se, por outro lado, o ouvinte mantiver uma referência fraca a esse objeto, como o nome sugere, esse construto corre o risco de ser limpo às vezes em que não deveria. Os ciclos de vida do objeto não são a ferramenta certa para implementar a semântica do aplicativo.iirc - você pode usar o método finalize como um meio de implementar um mecanismo de agrupamento para recursos caros - para que eles não obtenham GCs também.
fonte
Como uma nota rodapé:
Fonte: finalize () descontinuado em java-9
fonte
Os recursos (Arquivo, Soquete, Fluxo etc.) precisam ser fechados assim que terminarmos com eles. Eles geralmente têm um
close()
método que geralmente chamamos nafinally
seção detry-catch
declarações. Às vezes,finalize()
também pode ser usado por poucos desenvolvedores, mas a IMO não é uma maneira adequada, pois não há garantia de que finalize será chamado sempre.No Java 7, temos a instrução try-with-resources , que pode ser usada como:
No exemplo acima, o try-with-resource fechará o recurso automaticamente
BufferedReader
, invocando oclose()
método Se quisermos, também podemos implementar o Closeable em nossas próprias classes e usá-lo de maneira semelhante. OMI parece mais arrumado e simples de entender.fonte
Pessoalmente, quase nunca usei,
finalize()
exceto em uma circunstância rara: fiz uma coleção personalizada do tipo genérico e escrevi umfinalize()
método personalizado que faz o seguinte:(
CompleteObject
É uma interface que eu fiz, que permite especificar que você implementou raramente implementadosObject
métodos como#finalize()
,#hashCode()
, e#clone()
)Portanto, usando um
#setDestructivelyFinalizes(boolean)
método irmão , o programa que usa minha coleção pode (ajudar) garantir que a destruição de uma referência a essa coleção também destrua referências a seu conteúdo e descarte todas as janelas que possam manter a JVM ativa sem intenção. Também considerei interromper qualquer thread, mas isso abriu uma nova lata de worms.fonte
finalize()
de suasCompleteObject
instâncias, como você faz no código acima, implica que ela pode ser chamada duas vezes, pois a JVM ainda pode invocarfinalize()
quando esses objetos se tornarem inacessíveis, o que, devido à lógica da finalização, pode estar anterior aofinalize()
método de sua coleção especial é chamado ou até simultaneamente ao mesmo tempo. Desde que eu não vejo quaisquer medidas para garantir a segurança do thread em seu método, você parece não ter consciência disso ...A resposta aceita lista que o fechamento de um recurso durante a finalização pode ser feito.
No entanto, esta resposta mostra que, pelo menos no java8 com o compilador JIT, você enfrenta problemas inesperados nos quais, às vezes, o finalizador é chamado antes mesmo de terminar a leitura de um fluxo mantido pelo seu objeto.
Portanto, mesmo nessa situação, chamar finalize não seria recomendado.
fonte