1. A importância do mecanismo de reciclagem de lixo Uma característica notável da linguagem Java é a introdução de um mecanismo de coleta de lixo, que resolve o problema mais problemático de gerenciamento de memória para programadores C++, de modo que programadores Java não precisam mais considerar o gerenciamento de memória ao escrever programas. Devido ao mecanismo de coleta de lixo, objetos em Java não possuem mais o conceito de "escopo", apenas a referência do objeto tem um "escopo".A coleta de lixo pode prevenir efetivamente o vazamento de memória e usar efetivamente a memória ociosa.
ps:内存泄露是指该内存空间使用完毕之后未回收,在不涉及复杂数据结构的一般情况下,Java 的内存泄露表现为一个内存对象的生命周期超出了程序需要它的时间长度,我们有时也将其称为“对象游离”。
2. Algoritmos em mecanismos de coleta de lixo A especificação da linguagem Java não especifica explicitamente qual algoritmo de coleta de lixo usar na JVM, mas qualquer algoritmo de coleta de lixo geralmente precisa fazer duas coisas básicas: (1) encontrar objetos de informação inúteis; (2) Recuperar o espaço de memória ocupado por objetos inúteis, para que o espaço possa ser usado novamente pelo programa.
1. Coletor de Contagem de Referências 1.1 Análise de algoritmos
A contagem de referências é uma estratégia inicial em coletores de lixo. Nessa abordagem, há uma contagem de referência para cada instância de objeto no heap. Quando um objeto é criado e a instância do objeto é atribuída a uma variável, a contagem de variáveis é definida para 1. Quando qualquer outra variável é atribuída como referência a esse objeto, a contagem é somada por 1 (a = b, então o contador da instância do objeto referenciada por b é +1), mas quando uma referência a uma instância de objeto excedeu seu tempo de vida ou é definida para um novo valor, o contador de referência da instância do objeto é subtraído por 1. Qualquer instância de um objeto com contador de referência 0 pode ser coletada como lixo. Quando uma instância de objeto é coletada por lixo, o contador de referência de qualquer instância de objeto que ela referencia é menos 1. 1.2 Vantagens e desvantagens Mérito:
O coletor de contagem de referência pode ser executado muito rapidamente, entrelaçado na execução do programa. É vantajoso para ambientes em tempo real onde os programas não precisam ser interrompidos por longos períodos. Deficiência:
Referências circulares não podem ser detectadas. *Se o objeto pai tem referência a um objeto filho, o objeto filho por sua vez faz referência ao objeto pai. Dessa forma, a contagem de citações nunca pode ser 0. 1.3 O algoritmo de contagem de referências não pode resolver o problema de referência circular, por exemplo:
/** * Java学习交流QQ群:589809992 我们一起学Java! */public class Main { public static void main(String[] args) { MyObject object1 = new MyObject(); MyObject object2 = new MyObject(); object1.object = object2; object2.object = object1; object1 = null; object2 = null; }} As duas últimas frases atribuem object1 e object2 a null, o que significa que os objetos apontados por object1 e object2 não podem mais ser acessados, mas como se referem entre si, seus contadores de referência não são 0, então o coletor de lixo nunca os reciclará.
2. Coletor de rastreamento ou algoritmo de marcação e varrimento 2.1 Algoritmo de busca raiz
O algoritmo de busca por raiz é introduzido a partir da teoria dos grafos em matemática discreta; o programa considera todas as relações de referência como um grafo, começando a partir de um nó GC ROOT, procurando o nó de referência correspondente; após encontrar esse nó, continuando a procurar pelo nó de referência desse nó; quando todos os nós de referência forem pesquisados, os nós restantes são considerados nós não referenciados, ou seja, nós inúteis. java que pode ser usado como GC Root 1. Objetos referenciados na pilha de máquinas virtuais (tabela de variáveis locais) 2. O objeto referenciado pelo atributo estático na área do método 3. O objeto referenciado pela constante na área do método 4. Objetos referenciados na pilha de métodos local (objetos nativos)
2.2 Diagrama esquemático do algoritmo de traçado
![]()
2.3 Análise do algoritmo de limpeza de marcadores
O algoritmo de purga de tags escaneia a coleção raiz, marca os objetos sobreviventes e então escaneia todo o espaço em busca de objetos não marcados para reciclar, como mostrado na figura acima. O algoritmo de tag-purge não precisa mover objetos e processa apenas objetos não sobreviventes, o que é extremamente eficiente quando há muitos objetos sobreviventes, mas como o algoritmo de tag-purge recicla diretamente objetos não sobreviventes, isso causará fragmentação de memória.
3. Algoritmo de compactação ou algoritmo de finalização de rótulos
![]()
O algoritmo tag-finish usa o mesmo método do algoritmo de limpeza de tags para rotular objetos, mas é diferente ao purgar: após recuperar o espaço ocupado por objetos não sobreviventes, ele move todos os objetos sobreviventes para o espaço livre na extremidade esquerda e atualiza o ponteiro correspondente. O algoritmo tag-finish é baseado no algoritmo tag-purge e move objetos, sendo então mais caro, mas resolve o problema da fragmentação da memória. Na implementação de coletores baseados no algoritmo de compactação, geralmente são adicionadas tabelas de alças e alças.
4. Algoritmo de Cópia (Coletor de Compactação)
![]()
O algoritmo é proposto para superar a sobrecarga da alavanca e resolver a coleta de lixo de detritos do heap. Quando o objeto está cheio, a coleta de lixo baseada no algoritmo de cópia escaneia o objeto ativo do conjunto raiz e copia cada objeto ativo para a face livre (de modo que não haja buracos livres entre a memória ocupada pelo objeto ativo), de modo que a superfície livre se torne a face do objeto, a face original do objeto se torne a face livre, e o programa aloque memória na nova face do objeto. Uma coleta de lixo típica baseada no algoritmo coping é o algoritmo stop-and-copy, que divide o heap em faces de objeto e faces de área livre, e o programa pausa a execução durante a troca entre faces de objeto e faces de área livre.
5. Colecionador Geracional
![]()
A estratégia geracional de reciclagem de lixo baseia-se no fato de queO ciclo de vida de diferentes objetos é diferente。 Portanto, objetos com diferentes ciclos de vida podem adotar diferentes algoritmos de reciclagem para melhorar a eficiência da reciclagem.
Geração Jovem 1. Todos os objetos recém-gerados são colocados primeiro na geração mais jovem. O objetivo da geração mais jovem é coletar esses objetos com um ciclo de vida curto o mais rápido possível.
2. A memória da nova geração é dividida em uma região do Éden e duas zonas de sobreviventes (sobrevivente0, sobrevivente1) de acordo com a proporção de 8:1:1. Uma zona Éden, duas zonas de Sobreviventes (em geral). A maioria dos objetos é gerada na área do Éden. Quando a área do survivor0 também está cheia, a área do Éden e a área do Survivor 0 são copiadas para outra área do Survivor1, e então as áreas do Eden e do Survivor0 são esvaziadas, e então a área do Survivor0 fica vazia, e então a área do Survivor0 e a área do Survivor1 são trocadas. Ou seja, manter a área do sobrevivente vazia, e assim por diante.
3. Quando a área do sobrevivente 1 não é suficiente para armazenar os objetos sobreviventes do Éden e do Sobrevivente0, os objetos sobreviventes são armazenados diretamente na era antiga. Se a velhice também estiver cheia, isso desencadeará um GC completo, ou seja, a nova geração e a geração antiga serão recicladas
4. A GC da nova geração também é chamada de GC Menor, e a frequência da GC Menor é relativamente alta (não necessariamente é acionada quando a área do Eden está cheia)
Geração Antiga
1. Objetos que ainda estão vivos após experimentar a reciclagem de lixo N na geração mais jovem serão colocados na geração mais velha. Portanto, pode-se considerar que a geração antiga está armazenada em alguns objetos com um longo ciclo de vida.
2. A memória também é muito maior do que a da nova geração (a razão aproximada é 1:2), quando a memória da idade avançada está cheia, a GC Maior é acionada, ou seja, GC Completa, a frequência da GC Completa é relativamente baixa, o tempo de sobrevivência do objeto da idade antiga é relativamente longo e a marca da taxa de sobrevivência é alta.
Geração Permanente Usado para armazenar arquivos estáticos, como classes Java, métodos, etc. Gerações persistentes não têm impacto significativo na coleta de lixo, mas algumas aplicações podem gerar ou chamar dinamicamente algumas classes, como Hibernate, etc., e, nesse caso, um espaço de geração persistente relativamente grande precisa ser configurado para armazenar essas novas classes durante o tempo de execução.
3. GC (Coletor de Lixo)Colecionadores usados pelos colecionadores da nova geração: Serial, PraNew, Parallel Scavenge Colecionadores usados por colecionadores de velhices: Serial Old, Parallel Old, CMS
![]()
Coletor serial (algoritmo de replicação) A nova geração de coletores de rosca única, marcação e limpeza são de rosca única, com a vantagem de serem simples e eficientes.
Coletor Antigo Serial (Algoritmo de Acabamento de Rótulos) Coletor de Fios Únicos Antigos, Versão Antiga do Colecionador Serial.
Coletor ParNew (algoritmo de cópia e parar) O coletor de nova geração pode ser considerado uma versão multithreaded do coletor Serial, que tem desempenho melhor do que o Serial em um ambiente de CPU multi-core.
Coletor de Captura Paralelo (Algoritmo de Stop-Copy) Coletores paralelos para alta taxa de transferência e utilização eficiente da CPU. A taxa de transferência é geralmente de 99%, e a taxa = tempo de thread do usuário / (tempo de thread do usuário + tempo de thread GC). É adequado para cenários como aplicações em segundo plano que não exigem alta interação correspondentemente.
Coletor Antigo Paralelo (Algoritmo de Cópia Stop-Copy) Uma versão mais antiga do coletor Parallel Scavenge, um coletor paralelo, com prioridade de throughput
Coletor CMS (Sweep Concorrente de Marcação) (Algoritmo de Limpeza de Marca) Alta concorrência, baixa pausa, busca pelo menor tempo de pausa de recuperação do GC, alto uso de CPU, tempo de resposta rápido, tempo de pausa curto e CPU multi-core buscando alto tempo de resposta
4. O mecanismo de implementação da GCComo o objeto foi processado em diferentes gerações, a área e o momento da coleta de lixo também são diferentes. Existem dois tipos de GC: GC de Scavenge e GC completo.
Scavenge GC Normalmente, quando um novo objeto é gerado e não solicita espaço no Éden, o GC de Scavenge é ativado, que GC a área do Eden, elimina os objetos não sobreviventes e move os objetos sobreviventes para a área do Sobrevivente. Depois, organize as duas zonas do Survivor. Dessa forma, a GC é realizada na área do Eden da geração mais jovem e não afeta a geração mais velha. Como a maioria dos objetos começa na área do Éden, e a área do Éden não é muito alocada, a GC da área do Éden é realizada frequentemente. Portanto, geralmente é necessário usar algoritmos rápidos e eficientes aqui para tornar o Eden livre o mais rápido possível.
Classificação geral completa Organize toda a pilha, incluindo Young, Tenured e Perm. O GC completo é mais lento que o GC de Scavenge porque requer reciclar todo o heap, então o número de GCs completos deve ser reduzido o máximo possível. Uma grande parte do processo de afinação da JVM é a afinação do FullGC. A GC completa pode ser causada pelos seguintes motivos: 1. Estabilidade é escrita na íntegra 2. Perm é escrito completo 3. System.gc() é exibido como uma chamada 4. A estratégia de alocação de domínio do Heap muda dinamicamente após a última GC
5. Java com GC também terá problemas de vazamento de memória1. O uso de classes de coleta estática, como HashMap, Vector, etc., é a mais propensa a vazamento de memória, e o ciclo de vida dessas variáveis estáticas é o mesmo do aplicativo, e todos os objetos não podem ser liberados, pois sempre serão aplicados pelo Vector e outros. Vetor estático v = novo Vetor(); para (int i = 1; i<100; i++) { Objeto o = novo Objeto(); v.add(o); o = nulo; } Neste exemplo, há uma referência v para o objeto Vector e uma referência o para o objeto Object na pilha de códigos. No ciclo For, continuamos gerando novos objetos, depois adicionando-os ao objeto Vector e anulando a referência o. A questão é: quando a referência O for anulada, se o GC ocorrer, o objeto Object que criamos pode ser reciclado pelo GC? A resposta é não. Porque quando o GC rastreia referências na pilha de código, ele encontra v referências, e se você continuar rastreando, verá que há referências a objetos Objeto no espaço de memória apontadas por v referências. Isso significa que, mesmo que a referência o tenha sido esvaziada, ainda existem outras referências ao objeto Objeto que podem ser acessadas, então o GC não pode liberá-las. Se após esse loop, o objeto objeto não tiver efeito sobre o programa, então assumimos que o programa Java tem um vazamento de memória. 2. Várias conexões, conexões de banco de dados, conexões de rede, conexões de entrada, etc., não mostram a chamada próxima ao fechamento e não são recicladas pelo GC, resultando em vazamento de memória. 3. O uso de ouvintes também pode causar vazamento de memória quando o ouvinte não é excluído ao liberar o objeto.
|