Um excelente programador Java deve entender como o GC funciona, como otimizar o desempenho da GC e como ter interação limitada com o GC, porque alguns aplicativos têm requisitos de alto desempenho, como sistemas incorporados, sistemas em tempo real etc., que só podem ser Melhor de maneira abrangente. Este artigo apresenta brevemente o princípio de trabalho do GC e, em seguida, realiza discussões detalhadas sobre várias questões-chave do GC e finalmente apresenta algumas sugestões de programação Java para melhorar o desempenho dos programas Java da perspectiva do GC.
Os princípios básicos do GC
O gerenciamento de memória de Java é na verdade o gerenciamento de objetos, incluindo a alocação e liberação de objetos.
Para programadores, a nova palavra -chave é usada para alocar objetos; .GC será responsável por recuperar o espaço de memória de todos /"inacessíveis /".
Para o GC, quando um programador cria um objeto, o GC começa a monitorar o endereço, o tamanho e o uso do objeto. Geralmente, o GC usa gráficos direcionados para gravar e gerenciar todos os objetos na heap (heap) (consulte a referência 1 para obter detalhes). Dessa forma, quais objetos são /"alcançáveis /" e quais objetos são /"inacessíveis /". No entanto, para garantir que o GC possa ser implementado em diferentes plataformas, as especificações do Java não estipulam estritamente muitos comportamentos do GC. Por exemplo, não há regulamentos claros sobre questões importantes, como que tipo de algoritmo de reciclagem usar e quando executar a reciclagem. Portanto, diferentes implementadores de JVM geralmente têm algoritmos de implementação diferentes. Isso também traz incerteza ao desenvolvimento dos programadores Java. Este artigo examina várias questões relacionadas ao trabalho do GC e se esforça para reduzir o impacto negativo dessa incerteza nos programas Java.
GC incremental (GC incremental)
O GC geralmente é implementado na JVM por um ou em um grupo de processos. Portanto, quando o GC é executado por um longo tempo, o usuário pode sentir a pausa do programa Java. Ainda há muitos objetos que devem ser reciclados que não foram reciclados. Portanto, ao projetar GC, é necessário pesar o tempo de pausa e a taxa de recuperação. Uma boa implementação do GC permite que os usuários definam as configurações de que precisam. Outros jogos on-line em tempo real não podem permitir interrupções de longo prazo no programa. O GC incremental é dividir uma interrupção de longo prazo em muitas pequenas interrupções através de um determinado algoritmo de reciclagem, reduzindo assim o impacto do GC nos programas de usuário. Embora o GC incremental possa não ser tão eficiente quanto um GC regular no desempenho geral, ele pode reduzir o tempo máximo de pausa do programa.
A JVM de hotspot fornecida pelo SUN JDK pode suportar GC incremental. A implementação do GC incremental do Hotspot JVM é usar o algoritmo GC do trem. Sua idéia básica é agrupar todos os objetos na pilha (hierarquicamente) de acordo com a criação e o uso, colocar objetos com uso frequente e altamente relevante em uma fila e continuar agrupando enquanto o programa executa o ajuste. Quando o GC é executado, ele sempre recicla os objetos mais antigos (raramente visitados recentemente) primeiro e, se o grupo inteiro for objetos recicláveis, o GC recicla todo o grupo. Dessa forma, cada execução do GC reciclará apenas uma certa proporção de objetos inacessíveis para garantir a operação suave do programa.
Explicação detalhada da função finalizada
Finalize é um método localizado na classe de objeto. Como a função Finalize não implementa automaticamente as chamadas da cadeia, devemos implementá -las manualmente, para que a última declaração da função finalizada seja geralmente super.Finalize (). Dessa forma, podemos implementar a chamada para implementar finalizar de baixo para cima, ou seja, primeiro lançar nossos próprios recursos e depois lançar os recursos da classe pai.
De acordo com a especificação do idioma Java, a JVM garante que esse objeto seja inacessível antes de chamar a função finalizada, mas a JVM não garante que essa função seja chamada. Além disso, a especificação também garante que a função finalizada seja executada no máximo uma vez.
Muitos iniciantes em Java pensam que esse método é semelhante ao destruidor em C ++ e coloca a liberação de muitos objetos e recursos nessa função. Na verdade, isso não é uma boa maneira. Existem três razões. Em segundo lugar, após a conclusão da execução da finalização, o objeto pode se tornar acessível e o GC precisa verificar novamente se o objeto é acessível. Portanto, o uso do Finalize reduzirá o desempenho do GC. Terceiro, desde o momento em que o GC chama Finalize é incerto, também é incerto liberar recursos dessa maneira.
Geralmente, a Finalize é usada para liberar alguns recursos que não são fáceis de controlar e são muito importantes, como algumas operações de E/S e conexões de dados. A liberação desses recursos é muito crítica para todo o aplicativo. Nesse caso, os programadores devem gerenciar principalmente esses recursos através do próprio programa (incluindo a liberação) e complementar o método de liberação de recursos com a função finalizada como um suplemento para formar um mecanismo de gerenciamento de seguros duplo, em vez de depender apenas para finalizar para liberar recursos.
Aqui está um exemplo para ilustrar que, depois que a função finalizada é chamada, ela ainda pode ser acessada.
classe MyObject {Test Main; /Restaure este objeto para que esse objeto possa atingir o sistema.out.println (/"Isso é finalizado/"); // usado para testar finalizar apenas uma vez}} classe teste {myObject ref; ] args) {teste de teste = novo (); .ref! = null) System.out.println (/"Meu objeto ainda está vivo/");
Resultados em execução:
Isso está finalizado
MyObject ainda está vivo
Neste exemplo, deve -se notar que, embora o objeto MyObject se torne um objeto acessível para finalizar, na próxima vez que você reciclar, a finalização não será mais chamada porque a função finalizada é chamada apenas uma vez no máximo.
Como o programa interage com o GC
O Java2 aprimora as funções de gerenciamento de memória e adiciona um pacote java.lang.ref, que define três classes de referência. Essas três classes de referência são Softreference, Referência Fraquesada e Phantomreference. Os pontos fortes de referência dessas classes de referência estão entre objetos acessíveis e inacessíveis.
Também é muito fácil criar um objeto de referência Defina a referência normal como NULL. Ao mesmo tempo, chamamos esse objeto de objeto de referência suave.
A principal característica da referência soft é que ele possui fortes funções de citação. Esse tipo de memória é reciclado apenas quando há memória insuficiente; portanto, quando há memória suficiente, elas geralmente não são recicladas. Além disso, esses objetos de referência também podem ser definidos como NULL antes que o Java lança uma exceção da Memória A seguir, o seguinte use pseudocódigo para este tipo de referência;
// Aplicativo Uma imagem do objeto Imagem = new Image (); // Crie objeto de imagem ... // Use a imagem ... // Depois de usar a imagem, defina -o para o tipo de referência soft e libere uma referência forte; = novo SoftReference (imagem); Ele precisa ser recarregado;
A maior diferença entre objetos de referência fraca e objetos de referência suave é que, quando o GC recicla, ele precisa usar um algoritmo para verificar se os objetos de referência soft são reciclados, enquanto o GC sempre recicla objetos de referência fracos. Objetos de referência fracos são mais fáceis e mais rápidos a serem reciclados pelo GC. Embora o GC deva reciclar objetos fracos ao executar, um grupo de objeto fraco com relacionamentos complexos geralmente exige que várias execuções do GC sejam concluídas. Os objetos de referência fracos são frequentemente usados nas estruturas do mapa para se referir a objetos com grandes volumes de dados.
As referências fantasmas são menos úteis e são usadas principalmente para ajudar no uso de funções finalizadas. Os objetos fantasmas se referem a alguns objetos que concluíram a função finalizada e são objetos inacessíveis, mas não foram reciclados pelo GC. Esse tipo de objeto pode ajudar a finalizar em algum trabalho de reciclagem posterior.
Algumas sugestões de codificação Java
De acordo com o princípio de trabalho do GC, podemos usar algumas habilidades e métodos para fazer com que o GC seja executado com mais eficiência e mais alinhamento com os requisitos do aplicativo. Aqui estão algumas sugestões para programação.
1. A sugestão mais básica é liberar as referências de objetos inúteis o mais rápido possível. Quando a maioria dos programadores usa variáveis temporárias, eles definirão automaticamente a variável de referência como NULL após a saída do domínio ativo (escopo). Gráficos, etc., têm uma relação complexa entre esses objetos. Para esses objetos, o GC os recicla geralmente é menos eficiente. Se o programa permitir, atribua o objeto de referência não utilizado a NULL o mais rápido possível. [Página]
2. Tente usar a função finalizada o mínimo possível. A função Finalize é uma oportunidade para o Java proporcionar aos programadores a oportunidade de liberar objetos ou recursos. No entanto, aumentará a carga de trabalho do GC; portanto, tente usar o Finalize o mínimo possível para reciclar recursos.
3. Se você precisar usar imagens usadas com frequência, poderá usar o tipo de aplicativo suave. Ele pode salvar a imagem na memória o máximo possível para o programa chamar sem causar o OutfMemory.
4. Preste atenção aos tipos de dados de coleta, incluindo estruturas de dados, como matrizes, árvores, gráficos e listas vinculadas. Além disso, preste atenção a algumas variáveis globais, bem como a algumas variáveis estáticas. Essas variáveis tendem a causar facilmente referências pendentes, causando resíduos de memória.
5. Quando o programa tiver um certo tempo de espera, o programador pode executar manualmente o System.GC () para notificar o GC para executar, mas a especificação do idioma Java não garante que o GC seja executado. O uso do GC incremental pode reduzir o tempo de pausa dos programas Java.