Em Java, seu gerenciamento de memória inclui dois aspectos: alocação de memória (ao criar objetos Java) e reciclagem de memória. Ambos os aspectos do trabalho são concluídos automaticamente pela JVM, reduzindo a dificuldade de aprender para programadores Java e evitar o perigo de operar diretamente a memória como C/C ++. No entanto, é precisamente porque o gerenciamento de memória é totalmente tratado pela JVM que muitos programadores Java não se importam mais com a alocação de memória, resultando em muitos programas sendo ineficientes e consumindo memória. Portanto, os programadores Java devem entender a JVM no final, a fim de escrever programas mais eficientes e fazer pleno uso de memória limitada.
1. Estado de Java na memória
Primeiro, vamos escrever um código como exemplo:
Person.java
teste de pacote; importar java.io.Serializable; Public Class Pessoa implementa serializável {estático final serialversionuid = 1L; Nome da string; // Nome da pessoa amigo; // Friends public Person () {} public Person (Nome da String) {super (); this.name = nome; }} Test.java
teste de pacote; public class Test {public static void main (string [] args) {pessoa p1 = nova pessoa ("Kevin"); Pessoa P2 = nova pessoa ("chuva"); Pessoa p3 = nova pessoa ("ensolarada"); p1.friend = p2; p3 = p2; p2 = nulo; }} Se você desenhar a referência do objeto no aspecto principal do teste.Java acima em um diagrama de referência de objeto a partir do método principal, é assim (os vértices são objetos e referências, e as arestas direcionadas são relações de referência):
Quando o programa é executado, depois de ser considerado um gráfico direcionado, ele pode ser dividido em três tipos:
1) Estado alcançável: Depois que um objeto é criado, mais de uma variável de referência se refere a ele. Em um gráfico direcionado, você pode navegar para o objeto a partir do vértice inicial e está em um estado acessível.
2) Estado recuperável: se um objeto no programa não tiver mais variáveis de referência referentes a ele, ele primeiro entrará no estado restaurável e, neste momento, ele não poderá navegar para o objeto a partir do vértice inicial do gráfico direcionado. Nesse estado, o mecanismo de coleta de lixo do sistema está pronto para reciclar a memória ocupada pelo objeto. Antes da reciclagem, o sistema chamará o método finalize () para limpar o recurso. Se mais de uma variável de referência for recuperada após a classificação do recurso, o objeto se tornará um estado acessível novamente; Caso contrário, ele entrará em um estado inacessível.
3) Estado inacessível: quando todas as associações do objeto são cortadas e o sistema chama o método Finalize () para limpar o recurso ainda não torna o estado acessível do objeto, o objeto perderá permanentemente a referência e se tornará um estado inacessível, e o sistema realmente reciclará os recursos ocupados pelo objeto.
O diagrama de transição dos três estados acima é o seguinte:
2. 4 Referências a objetos por Java
1) Referência forte: Crie um objeto e atribua esse objeto diretamente a uma variável, por exemplo: pessoa pessoa = nova pessoa ("Sunny"); Não importa quão rígidos os recursos do sistema sejam, o forte objeto referenciado nunca será reciclado, mesmo que não seja usado novamente no futuro.
2) Referência suave: implementada através da classe SoftReference, por exemplo: SoftReference <Say> p = nova Softreference <Pesso> (nova pessoa ("chuva"));, quando a memória for muito apertada, ela será reciclada e não será reciclada.
3) Referência fraca: implementada através da classe de referência fraca, por exemplo: Referência fraca <SOYSE> p = nova fraco Reference <Pesso> (nova pessoa ("chuva")); Independentemente de a memória ser suficiente, o sistema será definitivamente reciclado durante a coleta de lixo.
4) Citação virtual: não pode ser usada sozinha, é usado principalmente para rastrear o estado do objeto sendo coletado de lixo. Implementado através da classe Phantomreference e da classe de referência de referência de referência, por exemplo:
teste de pacote; importar java.lang.ref.phantomreference; importar java.lang.ref.referencequeue; public class Test {public static void main (string [] args) {// crie uma pessoa objeto pessoa = nova pessoa ("ensolarado"); // Crie uma fila de referência ReferenceQueue <SOSE> rq = new ReferenceQueue <SOmeS> (); // Crie uma referência virtual, deixe essa referência virtual de referência ao objeto Pessoa fantasma <semeference> pr = new Phantomreference <Sestle> (Pessoa, RQ); // resolver as variáveis de referência e objetos da pessoa referências a pessoa = null; // Tente obter o objeto referenciado pela referência virtual // Descobri que o programa não pode acessar o objeto referenciado através da referência virtual; portanto, a saída aqui é NULL System.out.println (PR.Get ()); // Sistema de coleta de lixo forçado.gc (); System.Runfinalization (); // Como uma vez que o objeto na referência virtual seja reciclado, a referência virtual inserirá a fila de referência //, então use a referência primeiro digitou a fila na fila para comparar com o PR e a saída do System.out.println (rq.poll () == PR); }}Resultados em execução:
3. Mecanismo de coleta de lixo java
De fato, a coleção de lixo Java faz principalmente duas coisas: 1) Reciclagem de memória 2) Desafregmentação
3.1 Algoritmo de coleta de lixo
1) Reciclagem em série (apenas uma CPU) e reciclagem paralela (Múltiplas CPUs são úteis): a reciclagem em série significa que, não importa quantos CPUs o sistema tenha, é sempre apenas uma CPU para executar operações de coleta de lixo. A reciclagem paralela significa dividir todo o trabalho de reciclagem em várias partes, sendo cada parte responsável por uma CPU, para que várias CPUs possam ser recicladas em paralelo. A reciclagem paralela é muito eficiente na execução, mas aumenta a complexidade e também existem alguns efeitos colaterais, como aumento aleatório na memória.
2) Execução simultânea e parada de aplicação: Como o nome sugere, seu método de coleta de lixo causará suspensão do aplicativo durante a execução da coleta de lixo. Embora a coleta de lixo de execução simultânea não faça com que o aplicativo seja pausado, porque o lixo de execução simultâneo precisa resolver conflitos com o aplicativo (o aplicativo pode modificar objetos durante o processo de coleta de lixo), a sobrecarga do sistema de execução concorrente da coleta de lixo é maior que a do Stop-Thorld e requer mais memória de memória quando a execução.
3) Compressa e não compressa e cópia:
① O coletor de lixo que suporta compressão (Mark-Compression = Mark-Clear + Compressão) realocará todos os objetos acessíveis e, em seguida, reciclará toda a memória ocupada anteriormente, reduzindo a fragmentação da memória.
② O coletor de lixo não compactado (mark-clear) precisa ser percorrido duas vezes. A primeira vez que você acessa todos os objetos acessíveis e os marca como estados acessíveis. Na segunda vez que você facilita toda a área de memória e recicle objetos que não são estados acesos acentuados. Esse método de reciclagem não é comprimido e não requer memória extra, mas produzirá fragmentação se for necessário dois travessos.
③ Copie o coletor de lixo: divida a memória da heap em dois espaços idênticos, acesse cada objeto acessível associado da raiz (semelhante ao vértice inicial do gráfico direcionado anterior), copie todos os objetos alcançáveis no espaço A ao espaço B e depois recicle o espaço A ao mesmo tempo. Para esse algoritmo, porque você só precisa acessar todos os objetos acessíveis, copiar todos os objetos acessíveis e reciclar diretamente todo o espaço, ignorando os objetos inacessíveis, o custo de atravessar o espaço é pequeno, mas requer grandes custos de cópia e mais memória.
3.2 Reciclagem geracional da memória da heap
1) A base para a reciclagem geracional:
①O comprimento do tempo de sobrevivência do objeto: a maioria dos objetos é reciclada durante o período jovem - gerações diferentes adotam diferentes estratégias de reciclagem de lixo: Novo (tempo curto de sobrevivência) (tempo de sobrevivência longo) raramente tem referências entre objetos
2) Geração de memória de heap:
① Young Generation:
Ⅰ Mecanismo de reciclagem: como o número de objetos é pequeno, a replicação e a reciclagem são usadas.
Ⅱ Área de consolidação: consiste em 1 área de Éden e 2 áreas de sobrevivência. Duas áreas de sobrevivência ao mesmo tempo, uma é usada para salvar o objeto e o outro está vazio; Toda vez que a coleção de lixo jovem geração é realizada, os objetos acessíveis no Éden e de são copiados para a área, e alguns dos longos são copiados para a velhice, então o Éden e do espaço é limpo, e finalmente o original ao espaço se torna do espaço, e o original do espaço se torna para o espaço.
Ⅲ Fonte do objeto: a maioria dos objetos é atribuída primeiro à área do Éden, e alguns objetos grandes serão atribuídos diretamente à geração antiga.
Ⅳ Frequência de reciclagem: como a maioria dos objetos de geração jovem entra rapidamente em um estado inacessível, a frequência de reciclagem é alta e a velocidade de reciclagem é rápida.
②old Generation:
Ⅰ Mecanismo de reciclação: use o algoritmo de compressão de marca para se recuperar.
Ⅱ Fonte dos objetos: 1. O objeto grande entra diretamente na velhice.
2. Frequência de reciclagem de objetos acessíveis com longo tempo de sobrevivência na geração jovem: como poucos objetos morrem, a frequência de execução não é alta e leva muito tempo para ser concluído.
③ Generação Permanente:
ⅠPurse: Usado para carregar classe, método e outras informações. O padrão é 64m e não será reciclado. ⅡObject Fonte: por exemplo: para estruturas como Hibernate e Spring que, como as classes de geração dinâmica da AOP, geralmente geram um grande número de classes dinâmicas de proxy, para que seja necessária uma memória de geração mais permanente. Por isso, geralmente encontramos java.lang.outOfMemoryError: erro de espaço permgen ao depurar o hibernato. Este é o erro causado pela exaustão da memória de geração permanente.
Ⅲ Frequência de reciclagem: não será reciclado
3.3 coletores de lixo comuns
1) Reciclador em série (apenas uma CPU é usada): Young Generation usa o algoritmo de cópia serial; A geração antiga usa o algoritmo de compressão de marcas em série (três estágios: Mark Mark - varredura clara - compactar compacto), o programa será pausado durante o período de reciclagem.
2) Reciclador paralelo: O algoritmo usado para a geração jovem é o mesmo que o reciclador serial, mas adiciona apenas processamento paralelo de multi-CPU; O processamento da geração antiga é exatamente o mesmo que o do reciclador em série e ainda é um único thread.
3) Coletor de compressão paralela: O processamento da geração jovem é exatamente o mesmo algoritmo que o colecionador paralelo; Porém, algoritmos diferentes são usados para a geração antiga, que é realmente dividida em diferentes regiões e, em seguida, algoritmo de rotulagem e compressão:
① Divida antiga em várias áreas fixas;
② Marcar estágio (paralelo multithread), marcando objetos acessíveis;
③ Estágio de resumo (execução em série). Quando você encontra uma área que atinge o valor numérico (baixa densidade de objetos) da esquerda, essa área e sua área direita são compactadas e recuperadas. A extremidade esquerda é a área densa ④ estágio compacto (paralelo com vários thread), identifique as áreas que precisam ser carregadas e copie os dados nessas áreas em paralelo. Após esse processo, há um grande número de objetos ativos em uma extremidade da geração antiga e uma grande parte do espaço na outra extremidade.
4) Identificação Concorrente - Limpeza e Reciclagem (CMS): O processamento da geração jovem é exatamente o mesmo algoritmo que o dos recicladores paralelos; Mas algoritmos diferentes são usados para a geração antiga, mas o algoritmo de limpeza de marcas ainda é usado:
① Identificação inicial (pausa do programa): marca o objeto diretamente referenciado (objeto de primeiro nível);
② Identificação simultânea (execução do programa): encontre outros objetos acessíveis através de objetos de primeiro nível;
③ Re-mark (pausa do programa): Objetos paralelos de re-marcação de vários threads que podem ter sido perdidos devido à simultaneidade (simplesmente falando, é anti-falta
④ Limpeza simultânea (execuções do programa)
4. Dicas de gerenciamento de memória
1) Tente usar a quantidade direta, por exemplo: String javastr = "O processo de crescimento da aprendizagem da escola primária";
2) Use StringBuilder e StringBuffer para executar concatenação da String e outras operações;
3) liberar objetos inúteis o mais rápido possível;
4) Tente usar variáveis estáticas o mínimo possível;
5) Objetos de cache comumente usados: pode ser implementado usando cache de código aberto de código aberto, por exemplo: Oscache, ehcache;
6) Tente não usar o método finalize ();
7) Você pode considerar o uso de soft softreference quando necessário.
O exposto acima é todo o conteúdo deste artigo. Espero que seja útil para o aprendizado de todos e espero que todos apoiem mais o wulin.com.