Perguntas de entrevista JVM de 20.000 palavras com análise de resposta (resumo de 78 perguntas de entrevista JVM em 2020)

prefácio

Sobre os pontos de conhecimento das entrevistas da série JVM, resumi um mapa mental e compartilhei com você

1. Haverá um vazamento de memória em java? Descreva-o brevemente.

reunião. Vazamentos de memória podem ocorrer ao implementar você mesmo a estrutura de dados empilhados.

2. Na JVM de 64 bits, qual é o comprimento máximo de int?

Em Java, o comprimento de uma variável do tipo int é um valor fixo, que é de 32 bits, independentemente da plataforma. Ou seja, nas máquinas virtuais Java de 32 e 64 bits, o comprimento do tipo int é o mesmo.

3. Qual é a diferença entre GC Serial e Paralelo?

Ambos Serial e Parallel causarão stop-the-world quando o GC for executado. A principal diferença entre eles é que o coletor serial é o coletor de cópia padrão e há apenas um thread ao executar o GC, enquanto o coletor paralelo usa vários threads do GC para executar.

4. Para JVM de 32 bits e 64 bits, qual é o comprimento máximo da variável de tipo int?

Na JVM de 32 bits e 64 bits, o comprimento da variável do tipo int é o mesmo, ambos são 32 bits ou 4 bytes.

5. Qual é a diferença entre WeakReference e SoftReference em Java?

Embora WeakReference e SoftReference sejam benéficos para melhorar a eficiência do GC e da memória, uma vez que o WeakReference perde a última referência forte, ele será reciclado pelo GC, enquanto a referência suave não pode impedir que seja reciclada, mas pode ser adiada até que a JVM seja executada fora da memória.

6. O que a opção JVM -XX:+UseCompressedOops faz? por que usar

Ao migrar seu aplicativo de uma JVM de 32 bits para uma JVM de 64 bits, a memória heap aumentará repentinamente, quase dobrando, devido ao aumento nos ponteiros de objeto de 32 bits para 64 bits. Isso também afetará negativamente os dados no cache da CPU (que é muito menor que a RAM). Porque a principal motivação para migrar para uma JVM de 64 bits é que o tamanho máximo de heap pode ser especificado e uma certa quantidade de memória pode ser salva com a compactação de OOP. Com a opção -XX:+UseCompressedOops, a JVM usará OOP de 32 bits em vez de OOP de 64 bits.

7. Como julgar se a JVM é de 32 ou 64 bits por meio de um programa Java?

Você pode verificar algumas propriedades do sistema como sun.arch.data.model ou os.arch para obter essas informações.

8. Qual é a memória heap máxima da JVM de 32 bits e da JVM de 64 bits, respectivamente?

Em teoria, a memória heap da JVM de 32 bits pode atingir 2 ^ 32, que é 4 GB, mas na realidade será muito menor do que isso. Varia entre diferentes sistemas operacionais, como cerca de 1,5 GB para Windows e cerca de 3 GB para Solaris. A JVM de 64 bits permite especificar a memória heap máxima, que teoricamente pode chegar a 2^64, que é um número muito grande.Na verdade, você pode especificar o tamanho da memória heap para 100 GB. Mesmo algumas JVMs, como a Azul, possuem memória heap de até 1000G.

9. Qual a diferença entre JRE, JDK, JVM e JIT?

JRE significa Java run-time e é necessário para executar referências Java. JDK significa Java development kit (Kit de desenvolvimento Java), que é uma ferramenta de desenvolvimento de programas Java, como um compilador Java, que também inclui um JRE. JVM significa Java virtual machine (máquina virtual Java), que é responsável pela execução de aplicativos Java. JIT significa compilação just-in-time (compilação Just In Time). Quando o número de execuções de código exceder um determinado limite, ele converterá o bytecode Java em código nativo. Por exemplo, o hot code principal será substituído pelo código nativo, o que é benéfico Melhora significativamente o desempenho dos aplicativos Java.

10. Explique o espaço de heap Java e GC?

Quando um processo Java é iniciado pelo comando Java, a memória é alocada para ele. Parte da memória é usada para criar o espaço de heap e, quando um objeto é criado no programa, a memória é alocada a partir do espaço. GC é um processo dentro da JVM que recupera a memória de objetos inválidos para alocações futuras.

11. Área de memória da JVM

A área de memória JVM é dividida principalmente em área privada de encadeamento [contador de programa, pilha de máquina virtual, área de método local], área compartilhada de encadeamento [heap JAVA, área de método] e memória direta.

O ciclo de vida da área de dados privados do encadeamento é o mesmo do encadeamento e é criado/destruído dependendo do início/fim do encadeamento do usuário (no Hotspot VM, cada encadeamento é mapeado diretamente para o encadeamento local do o sistema operacional, de modo que o armazenamento/inexistência dessa parte da área de memória segue a correspondência de vida/morte do thread nativo).

A área compartilhada de thread é criada/destruída com a inicialização/desligamento da máquina virtual.

A memória direta não faz parte da área de dados de tempo de execução da JVM, mas também é usada com frequência: NIO introduzido no JDK 1.4 fornece um método IO baseado em Canal e Buffer, que pode usar a biblioteca de funções nativas para alocar diretamente a memória off-heap e, em seguida, use O objeto DirectByteBuffer opera como uma referência a essa memória (consulte: Extensões de E/S Java para obter detalhes), o que evita a cópia de dados entre o heap Java e o heap Nativo, portanto, o desempenho pode ser significativamente melhorado em alguns cenários.

12. Contador de programa (thread privado)

Um pequeno espaço de memória é o indicador do número da linha do bytecode executado pelo thread atual. Cada thread deve ter um contador de programa independente. Esse tipo de memória também é chamado de memória "thread private".

Se o método java estiver sendo executado, o que o contador registra é o endereço da instrução de bytecode da máquina virtual (o endereço da instrução atual). Se ainda for um método Nativo, estará vazio.

Essa região de memória é a única que não especifica nenhuma condição OutOfMemoryError na máquina virtual.

13. Pilha de máquina virtual (thread privado)

É um modelo de memória que descreve a execução de métodos java.Quando cada método é executado, um quadro de pilha (Stack Frame) é criado para armazenar informações como tabelas de variáveis ​​locais, pilhas de operandos, links dinâmicos e saídas de métodos. O processo de cada método, desde a invocação até a conclusão da execução, corresponde ao processo de um quadro de pilha sendo inserido na pilha da máquina virtual para sair da pilha.

Stack frame (Frame) é uma estrutura de dados usada para armazenar dados e alguns resultados do processo, e também é usada para lidar com vinculação dinâmica (Dynamic Linking), valor de retorno de método e despacho de exceção (Dispatch Exception). Os quadros de pilha são criados quando um método é chamado e destruídos quando o método termina - se o método é concluído normalmente ou anormalmente (gerando uma exceção que não foi capturada no método) conta como o fim do método.

14. Área de método local (thread privado)

A área de método local é semelhante à pilha Java, a diferença é que a pilha de máquina virtual serve para executar métodos Java, enquanto a pilha de método local serve para métodos nativos. então a pilha será A pilha C, mas HotSpot VM combina diretamente a pilha de método local e a pilha de máquina virtual em uma.

15. Você pode garantir a execução do GC?

Não, embora você possa chamar System.gc() ou Runtime.gc(), não há como garantir a execução do GC.

16. Como obter a memória utilizada pelo programa Java? % da pilha usada?

A memória restante, a memória total e a memória heap máxima podem ser obtidas por meio de métodos relacionados à memória na classe java.lang.Runtime. Você também pode obter a porcentagem de uso do heap e o espaço restante da memória do heap por meio desses métodos. O método Runtime.freeMemory() retorna o número de bytes do espaço restante, o método Runtime.totalMemory() retorna o número de bytes da memória total e o Runtime.maxMemory() retorna o número de bytes da memória máxima .

17. Qual é a diferença entre heap e stack em Java?

O heap e a pilha na JVM pertencem a diferentes áreas de memória e são usados ​​para diferentes propósitos. A pilha geralmente é usada para armazenar quadros de método e variáveis ​​locais, enquanto os objetos são sempre alocados no heap. A pilha geralmente é menor que o heap e não é compartilhada entre vários encadeamentos, enquanto o heap é compartilhado por todos os encadeamentos de toda a JVM.

18. Descreva o mecanismo principal dos arquivos de classe de carregamento da JVM

O carregamento de classes na JVM é implementado pelo carregador de classes (ClassLoader) e suas subclasses.Os vários carregadores em Java são um importante componente do sistema de tempo de execução Java, que é responsável por localizar e carregar arquivos de classes em tempo de execução. o tipo.

Devido à natureza de plataforma cruzada do Java, o programa de origem Java compilado não é um programa executável, mas um ou mais arquivos de classe. Quando um programa Java precisa usar uma determinada classe, a JVM garantirá que a classe foi carregada, conectada (validada, preparada e analisada) e inicializada. Carregamento de classe refere-se à leitura dos dados no arquivo .class da classe na memória, geralmente criando uma matriz de bytes e lendo-a no arquivo .class e, em seguida, gerando o objeto Class correspondente à classe carregada.

Após a conclusão do carregamento, o objeto Class não está completo, portanto, a classe neste momento ainda não está disponível. Quando a classe é carregada, ela entra na fase de conexão, que inclui três etapas: verificação, preparação (alocar memória para variáveis ​​estáticas e definir valores iniciais padrão) e resolução (substituir referências simbólicas por referências diretas). Finalmente, a JVM inicializa a classe, incluindo: 1) Se a classe tiver uma classe pai direta e a classe não tiver sido inicializada, inicialize a classe pai primeiro; 2) Se houver instruções de inicialização na classe, execute essas instruções de inicialização em sequência.

O carregamento de classe é feito por carregadores de classe, que incluem: carregador raiz (BootStrap), carregador de extensão (Extension), carregador de sistema (System) e carregador de classe definido pelo usuário (subclasse de java.lang.ClassLoader).

A partir do Java 2 (JDK 1.2), o processo de carregamento de classe adota o mecanismo de delegação pai (PDM). O PDM garante melhor a segurança da plataforma Java, neste mecanismo o Bootstrap que vem com a JVM é o root loader, e os demais loaders possuem e possuem apenas um class loader pai. O carregamento da classe primeiro solicita que o carregador de classes pai carregue e, quando o carregador de classes pai estiver indefeso, seu carregador de classes filho o carregará sozinho. A JVM não fornece referências ao Bootstrap para programas Java. O seguinte é sobre várias classes

Descrição do carregador:

(1) Bootstrap: geralmente implementado com código local, responsável por carregar a biblioteca de classes do núcleo básico da JVM (rt.jar);

(2) Extensão: Carregue a biblioteca de classes do diretório especificado pela propriedade do sistema java.ext.dirs e seu carregador pai é o Bootstrap;

(3) Sistema: Também conhecido como o carregador de classes do aplicativo, sua classe pai é Extension. É o carregador de classes mais amplamente utilizado. Obtém do classpath da variável de ambiente ou da propriedade do sistema

A classe registrada no diretório especificado por java.class.path é o carregador pai padrão do carregador definido pelo usuário.

19. O que é CG? Por que existe um CG?

GC significa coleta de lixo. A manipulação de memória é um lugar onde os programadores estão propensos a problemas. A recuperação de memória esquecida ou incorreta levará à instabilidade ou até mesmo à falha do programa ou sistema. A função GC fornecida pelo Java pode monitorar automaticamente se o objeto excede o escopo a ser alcançado Com o objetivo de recuperar memória automaticamente, a linguagem Java não fornece um método de operação explícito para liberar a memória alocada. Os programadores Java não precisam se preocupar com o gerenciamento de memória porque o coletor de lixo cuida disso automaticamente. Para solicitar a coleta de lixo, um dos seguintes métodos pode ser chamado: System.gc() ou Runtime.getRuntime().gc() , mas a JVM pode mascarar chamadas de coleta de lixo fora de linha.

A coleta de lixo pode efetivamente evitar vazamentos de memória e usar efetivamente a memória disponível. O coletor de lixo geralmente é executado como um thread separado de baixa prioridade. Sob circunstâncias imprevisíveis, os objetos que morreram ou não foram usados ​​por muito tempo no heap de memória são limpos e reciclados. Os programadores não podem chamar o coletor de lixo em tempo real. Um problema objeto ou todos os objetos são coletados como lixo. Nos primórdios do nascimento do Java, a coleta de lixo era um dos maiores destaques do Java, porque a programação do lado do servidor precisava efetivamente evitar vazamentos de memória. No entanto, com o passar do tempo, o mecanismo de coleta de lixo do Java tornou-se algo criticado . Os usuários de terminais móveis inteligentes geralmente acham que o sistema iOS oferece uma experiência de usuário melhor do que o sistema Android. Uma das razões profundas é a imprevisibilidade da coleta de lixo no sistema Android.

20. Heap (Heap-thread sharing) - área de dados em tempo de execução

É uma área de memória compartilhada por threads.Os objetos e arrays criados são armazenados na memória heap Java, e também é a área de memória mais importante para coleta de lixo pelo coletor de lixo. Como as VMs modernas usam algoritmos de coleta geracional, o heap Java também pode ser subdividido da perspectiva do GC: a nova geração (área Eden, área From Survivor e área To Survivor) e a geração antiga.

21. Área de método/geração permanente (compartilhamento de threads)

É o que costumamos chamar de Geração Permanente (Permanent Generation), que é utilizada para armazenar dados como informações de classes carregadas pela JVM, constantes, variáveis ​​estáticas e código compilado pelo compilador. HotSpot VM estende a coleta geracional GC para o método área, ou seja, a geração permanente do heap Java é utilizada para implementar a área do método, de forma que o coletor de lixo HotSpot possa gerenciar esta parte da memória da mesma forma que o heap Java, sem a necessidade de desenvolver um gerenciador de memória especial para a área do método (o principal objetivo da recuperação de memória permanente é a reciclagem constante do pool e o descarregamento do tipo, portanto, os ganhos geralmente são pequenos).

O Runtime Constant Pool faz parte da área de método. Além da versão da classe, campos, métodos, interfaces e outras descrições no arquivo Class, há também um pool de constantes (Constant Pool Table), que é usado para armazenar vários literais e referências de símbolos gerados durante a compilação. será armazenado no conjunto de constantes de tempo de execução na área de método após o carregamento da classe. A máquina virtual Java tem regras rígidas sobre o formato de cada parte do arquivo Class (incluindo o pool de constantes naturalmente). Os dados que cada byte é usado para armazenar devem atender aos requisitos da especificação, para que sejam reconhecidos pelo máquina virtual. Carregue e execute.

22. Memória de tempo de execução da JVM

Do ponto de vista do GC, o heap Java também pode ser subdividido em: a nova geração (área Eden, área From Survivor e área To Survivor) e a geração antiga.

23. A nova geração

É usado para armazenar novos objetos. Geralmente ocupam 1/3 do espaço da pilha. Devido à criação frequente de objetos, a nova geração frequentemente acionará o MinorGC para coleta de lixo. A nova geração é dividida em três áreas: Eden area, SurvivorFrom e SurvivorTo.

Distrito do Éden

O local de nascimento de novos objetos Java (se o objeto recém-criado ocupar muita memória, ele será alocado diretamente para a geração antiga). Quando a memória na área Eden não for suficiente, o MinorGC será acionado para realizar uma coleta de lixo na área de nova geração.

ServidorDe

Os sobreviventes do último GC são os únicos a serem verificados para este GC.

ServivorTo

Os sobreviventes de um processo MinorGC são mantidos.

O processo de MinorGC (copiar->limpar->trocar)

MinorGC usa um algoritmo de replicação.

(1) eden, servo ou De copiado para ServicorTo, idade +1

Primeiro, copie os objetos sobreviventes nas áreas Eden e SurvivorFrom para a área ServicorTo (se houver idade de um objeto e atingir o padrão de idade avançada, atribua-o à área de geração antiga) e, ao mesmo tempo, adicione 1 ao idade destes objectos (se o ServicorTo não tiver espaço coloque-o na zona dos idosos);

(2) Éden vazio, servicerFrom

Em seguida, limpe os objetos em Eden e ServicorFrom;

(3) Intercâmbio ServicorTo e ServicorFrom

Finalmente, ServicorTo e ServicorFrom são trocados, e o ServicorTo original torna-se a área ServicorFrom no próximo GC.

24. Velha geração

Ele armazena principalmente objetos de memória de longa duração no aplicativo.

Objetos na geração antiga são relativamente estáveis, então MajorGC não será executado com frequência. O MinorGC geralmente é executado antes do MajorGC, para que os objetos da nova geração sejam promovidos para a geração antiga e seja acionado quando não houver espaço suficiente. Quando for impossível encontrar um espaço contíguo grande o suficiente para alocar para objetos maiores recém-criados, ele também acionará um MajorGC com antecedência para a coleta de lixo liberar espaço.

O MajorGC usa um algoritmo de limpeza de marca: primeiro escaneie todas as idades antigas uma vez, marque os objetos sobreviventes e, em seguida, recicle os objetos não marcados. ajorGC leva muito tempo porque precisa ser escaneado e reciclado. MajorGC irá gerar fragmentação de memória. Para reduzir a perda de memória, geralmente precisamos mesclar ou marcá-la para alocação direta na próxima vez. Quando a geração antiga estiver muito cheia para caber, uma exceção OOM (Out of Memory) será lançada.

25. Geração permanente

Refere-se à área de armazenamento permanente da memória, que armazena principalmente as informações de Class e Meta (metadados). Quando a Class é carregada, ela é colocada na área permanente. É diferente da área onde as instâncias são armazenadas. GC não mudará a área permanente durante a execução do programa principal Área a ser limpa. Portanto, isso também faz com que a área da geração permanente fique cheia à medida que o número de classes carregadas aumenta, eventualmente lançando uma exceção OOM.

26. JAVA8 e metadados

No Java8, a geração permanente foi removida e substituída por uma área chamada "área de metadados" (metaespaço). A essência do metaespaço é semelhante à da geração permanente, a maior diferença entre o metaespaço e a geração permanente é que o metaespaço não está na máquina virtual, mas usa memória local. Portanto, por padrão, o tamanho do metaespaço é limitado apenas pela memória local. Os metadados da classe são colocados na memória nativa, o conjunto de strings e as variáveis ​​estáticas da classe são colocados no heap java, portanto, a quantidade de metadados da classe que pode ser carregada não é mais controlada pelo MaxPermSize, mas pelo espaço real disponível do sistema.

27. Contagem de referência

Em Java, referências e objetos são associados. Se você deseja manipular objetos, deve usar referências. Portanto, é óbvio que uma maneira simples é julgar se um objeto pode ser reciclado por contagem de referência. Simplificando, se um objeto não tiver nenhuma referência associada a ele, ou seja, suas contagens de referência não forem 0, isso significa que é improvável que o objeto seja usado novamente, então esse objeto é um objeto reciclável.

28. Análise de Acessibilidade

Para resolver o problema de referência circular do método de contagem de referência, Java usa o método de análise de acessibilidade. Pesquise uma série de objetos "raízes GC" como ponto de partida. Um objeto é considerado inacessível se não houver um caminho alcançável entre as "raízes GC" e o objeto. Ressalta-se que objetos inacessíveis não são equivalentes a objetos recicláveis, sendo necessários pelo menos dois processos de marcação para que objetos inacessíveis se tornem objetos recicláveis. Se ainda for um objeto reciclável após duas marcas, ele enfrentará a reciclagem.

29. Algoritmo Mark-Sweep (Mark-Sweep)

O algoritmo de coleta de lixo mais básico é dividido em dois estágios, marcação e limpeza. A fase de marcação marca todos os objetos que precisam ser recuperados e a fase de limpeza recupera o espaço ocupado pelos objetos marcados. como mostrado na foto

Pela figura, podemos ver que o maior problema desse algoritmo é que a fragmentação da memória é séria, e pode haver um problema de objetos grandes não conseguirem encontrar espaço disponível no futuro.

30. Algoritmo de cópia (cópia)

Um algoritmo proposto para resolver o defeito de fragmentação de memória do algoritmo Mark-Sweep. Divida a memória em dois blocos de tamanho igual de acordo com a capacidade de memória. Use apenas um deles de cada vez. Quando esta memória estiver cheia, copie os objetos sobreviventes para outro, e limpe a memória usada, conforme mostrado na figura:

Embora esse algoritmo seja simples de implementar, tenha alta eficiência de memória e não seja propenso a fragmentação, o maior problema é que a memória disponível é compactada para metade da original. E se o número de objetos sobreviventes aumentar, a eficiência do algoritmo de cópia será bastante reduzida.

31. Mark-Compact

Combinando os dois algoritmos acima, é proposto a fim de evitar defeitos. A fase de marcação é a mesma do algoritmo Mark-Sweep.Após a marcação, os objetos não são limpos, mas os objetos sobreviventes são movidos para uma extremidade da memória. Os objetos fora dos limites finais são então removidos. Como mostrado na imagem:

32. Algoritmo de coleta geracional

O método de coleta geracional é atualmente adotado pela maioria das JVMs. Sua ideia central é dividir a memória em diferentes domínios de acordo com os diferentes ciclos de vida dos objetos. Geralmente, o heap GC é dividido em geração antiga (Tenured/Old Generation) e The nova geração (YoungGeneration). A característica da geração antiga é que apenas um pequeno número de objetos precisa ser reciclado a cada coleta de lixo. A característica da nova geração é que uma grande quantidade de lixo precisa ser reciclada a cada coleta de lixo. Portanto, , diferentes algoritmos podem ser selecionados de acordo com diferentes regiões.

33. Algoritmo de Nova Geração e Replicação

Atualmente, o GC da maioria das JVMs adota o algoritmo Copying para a nova geração, pois cada coleta de lixo na nova geração precisa recuperar a maior parte dos objetos, ou seja, há menos operações a serem copiadas, mas a nova geração geralmente é não dividido de acordo com 1:1. Geralmente, a nova geração é dividida em um espaço Eden maior e dois espaços Survivor menores (From Space, To Space). Cada vez que o espaço Eden e um dos espaços Survivor são usados, ao reciclar, os dois espaços são Os objetos que são ainda vivos no espaço são copiados para outro espaço de Sobrevivente.

34. Algoritmo de Geração Antiga e Replicação de Marcas

A geração antiga usa o algoritmo Mark-Compact porque apenas um pequeno número de objetos é reciclado a cada vez.

(1) A Geração Permanet (Permanet Generation) na área de métodos mencionada pela máquina virtual JAVA é utilizada para armazenar classes, constantes, descrições de métodos, etc. A coleção da geração permanente inclui principalmente constantes obsoletas e classes inúteis.

(2) A alocação de memória de objetos é principalmente no Eden Space da nova geração e no From Space do Survivor Space (onde o Survivor atualmente armazena objetos) e, em alguns casos, será alocado diretamente para a geração anterior.

(3) Quando o Eden Space e From Space da nova geração forem insuficientes, ocorrerá um GC. Após o GC, os objetos sobreviventes nas áreas Eden Space e From Space serão movidos para To Space e, em seguida, Eden Space e From O espaço será limpo.

(4) Se To Space não puder armazenar um objeto o suficiente, armazene esse objeto na geração antiga.

(5) Depois de GC, Eden Space e To Space são usados, e o ciclo se repete.

(6) Quando o objeto escapar do GC uma vez no Survivor, sua idade será +1. Por padrão, os objetos cuja idade atinge 15 anos serão movidos para a geração antiga.

35. Referência forte de JAVA

A mais comum em Java é a referência forte, atribuindo um objeto a uma variável de referência, esta variável de referência é uma referência forte. Quando um objeto é referenciado por uma variável de referência forte, ele está em um estado alcançável e não pode ser reciclado pelo mecanismo de coleta de lixo, mesmo que o objeto nunca seja usado pela JVM no futuro. Portanto, referências fortes são uma das principais causas de vazamentos de memória Java.

36. Referência suave JAVA

As referências temporárias precisam ser implementadas com a classe SoftReference.Para um objeto com apenas referências temporárias, ele não será reciclado quando a memória do sistema for suficiente e será reciclado quando o espaço de memória do sistema for insuficiente. As referências suaves são freqüentemente usadas em programas sensíveis à memória.

37. Referência fraca de JAVA

As referências fracas precisam ser implementadas com a classe WeakReference, que tem um tempo de vida mais curto do que as referências soft. ocupado pelo objeto será sempre reciclado.Memória.

38. Referência virtual JAVA

A referência fantasma precisa ser implementada pela classe PhantomReference, que não pode ser usada sozinha, e deve ser usada em conjunto com a fila de referência. O principal objetivo das referências fantasmas é rastrear o status dos objetos que estão sendo coletados como lixo.

39. Algoritmo de coleta geracional

A atual coleta de lixo da VM mainstream adota o algoritmo "Generational Collection" (Generational Collection), que divide a memória em vários blocos de acordo com o ciclo de vida do objeto, como a nova geração, a geração antiga e a geração permanente na JVM. O algoritmo de GC mais adequado pode ser usado de acordo com as características de cada idade

40. No novo algoritmo de replicação de geração

Cada coleta de lixo pode descobrir que um grande número de objetos está morto e apenas um pequeno número deles está vivo. Portanto, se o algoritmo de cópia for selecionado, a coleta poderá ser concluída apenas pagando o custo de cópia de um pequeno número de sobreviventes objetos

41. Na velha geração - algoritmo de acabamento de marca

Como o objeto tem uma alta taxa de sobrevivência e não há espaço extra para sua garantia de alocação, é necessário usar o algoritmo "marcar-limpar" ou "marcar-organizar" para reciclar, sem copiar a memória, e liberar memória diretamente.

42. Algoritmo de coleta de partição

O algoritmo de partição divide todo o espaço de heap em diferentes pequenas áreas contínuas, cada uma das quais é usada e reciclada independentemente. A vantagem disso é que ele pode controlar quantas pequenas áreas são recicladas por vez e de acordo com o tempo de pausa desejado , um número razoável de pequenas áreas pode ser recuperado a cada vez Pequenas áreas (em vez do heap inteiro), reduzindo assim a pausa gerada por um GC.

43. Coletor de lixo GC

A memória heap Java é dividida em duas partes: a nova geração e a geração antiga. A nova geração usa principalmente o algoritmo de coleta de lixo de cópia e marcação e varredura; a geração antiga usa principalmente o algoritmo de coleta de lixo de classificação e marcação. Cada geração fornece um variedade de coletores de lixo diferentes. Os coletores de lixo da máquina virtual Sun HotSpot em JDK1.6 são os seguintes:

44. Coletor de lixo serial (single thread, algoritmo de cópia)

Serial (contínuo em inglês) é o coletor de lixo mais básico, usando o algoritmo de cópia, e foi o único coletor de lixo na nova geração anterior ao JDK1.3.1. Serial é um coletor de thread único. Ele não usa apenas uma CPU ou um thread para concluir o trabalho de coleta de lixo, mas também deve suspender todos os outros threads de trabalho até que a coleta de lixo termine.

Embora o coletor de lixo serial precise suspender todos os outros threads de trabalho durante a coleta de lixo, ele é simples e eficiente. Para um ambiente limitado de CPU única, não há sobrecarga de interação de thread e a mais alta eficiência de coleta de lixo de thread único pode ser obtida. Portanto , Lixo serial O coletor ainda é o coletor de lixo padrão de nova geração quando a máquina virtual java é executada no modo Cliente.

45. Coletor de Lixo ParNew (Serial + Multithreading)

O coletor de lixo ParNew é, na verdade, uma versão multithread do coletor serial e também usa o algoritmo de cópia. Exceto pelo uso de multithreading para coleta de lixo, o restante do comportamento é exatamente o mesmo do coletor serial. O coletor ParNew O coletor de lixo também faz o mesmo no processo de coleta de lixo, para suspender todos os outros threads de trabalho.

Por padrão, o coletor ParNew abre o mesmo número de encadeamentos que o número de CPUs, e o número de encadeamentos do coletor de lixo pode ser limitado pelo parâmetro -XX:ParallelGCThreads. [Paralelo: Paralelo]

Embora ParNew seja quase idêntico ao coletor Serial, exceto para multithreading, o coletor de lixo ParNew é o coletor de lixo padrão para a nova geração de muitas máquinas virtuais java em execução no modo Servidor.

46. ​​Coletor Parallel Scavenge (algoritmo de cópia multi-threaded, eficiente)

O coletor Parallel Scavenge também é um coletor de lixo de nova geração. Ele também usa o algoritmo de cópia e também é um coletor de lixo multi-thread. Ele se concentra no programa para obter uma taxa de transferência controlável (Thoughput, o tempo que a CPU é usada para executar código /CPU tempo de consumo total, ou seja, taxa de transferência = tempo de código de usuário em execução/(tempo de código de usuário em execução + tempo de coleta de lixo)), alta taxa de transferência pode fazer o uso mais eficiente do tempo de CPU e concluir as tarefas de cálculo do programa o mais rápido possível possível. Tarefas que operam em segundo plano sem exigir muita interação. A estratégia de ajuste adaptativo também é uma diferença importante entre o coletor ParallelScavenge e o coletor ParNew.

47. Coletor serial antigo (algoritmo de marcação de thread único)

Serial Old é a versão de geração antiga do coletor de lixo Serial. Também é um coletor de thread único que usa o algoritmo mark-sort. Esse coletor também é executado principalmente no cliente padrão

O coletor de lixo de geração antiga padrão para a máquina virtual java. No modo Servidor, existem duas finalidades principais:

(1) Usado em conjunto com a nova geração de coletores Parallel Scavenge em versões anteriores a JDK1.5.

(2) Como um esquema de coleta de lixo de backup usando o coletor CMS na geração antiga. O diagrama do processo de coleta de lixo da nova geração Serial e da velha colocação Serial Old:

O princípio de funcionamento do coletor Parallel Scavenge de nova geração é semelhante ao do coletor ParNew.Ambos são coletores multiencadeados, ambos usam o algoritmo de cópia e todos os encadeamentos de trabalho precisam ser suspensos durante o processo de coleta de lixo. O diagrama do processo de coleta de lixo da nova geração ParallelScavenge/ParNew e da velha geração Serial Old:

48. Coletor Antigo Paralelo (Algoritmo de Marcação Multithread)

O coletor Parallel Old é uma versão de geração antiga do Parallel Scavenge, que usa um algoritmo multi-threaded mark-sort, disponível apenas no JDK1.6.

Antes do JDK1.6, o coletor ParallelScavenge usado na nova geração só pode ser usado com o coletor Serial Old na geração antiga. Ele só pode garantir a taxa de transferência da nova geração primeiro, mas não pode garantir a taxa de transferência geral. Parallel Old é apenas para a geração antiga. Ele também fornece um coletor de lixo de throughput-first. Se o sistema tiver requisitos de throughput alto, você pode dar prioridade à estratégia de colocação da nova geração do Parallel Scavenge e do coletor Parallel Old da geração anterior.

O diagrama do processo de operação da nova geração Parallel Scavenge e da velha geração do coletor Parallel Old

49. Coletor CMS (algoritmo de varredura de marcação multithread)

O coletor Concurrent mark sweep (CMS) é um coletor de lixo de geração antiga cujo objetivo principal é obter o menor tempo de pausa na coleta de lixo. Ao contrário de outros algoritmos de classificação de marca de geração anterior, ele usa algoritmo de limpeza de marca multi-threaded. Pausas mínimas de coleta de lixo podem melhorar a experiência do usuário para programas altamente interativos. O mecanismo de trabalho do CMS é mais complicado do que o de outros coletores de lixo. Todo o processo é dividido em 4 etapas:

marca inicial

Basta marcar os objetos que podem ser associados diretamente ao GC Roots, o que é muito rápido e ainda precisa suspender todos os threads de trabalho.

marca concorrente

O processo de rastreamento de raízes do GC funciona com threads de usuário sem suspender threads de trabalho.

rotular novamente

Para corrigir o registro de marcação da parte do objeto cuja marcação muda devido à execução contínua do programa do usuário durante a marcação concorrente, ainda é necessário suspender todas as threads de trabalho.

limpeza simultânea

Limpe os objetos inacessíveis do GC Roots e trabalhe com os threads do usuário sem suspender os threads de trabalho. Como o encadeamento de coleta de lixo pode funcionar simultaneamente com o usuário durante o processo de marcação e limpeza simultâneos mais demorados, portanto, em geral, a recuperação de memória do coletor CMS e o encadeamento do usuário são executados simultaneamente. Processo de trabalho do coletor CMS

50. Coletor G1

O primeiro coletor de lixo Garbage é a conquista mais avançada no desenvolvimento da teoria do coletor de lixo. Comparado com o coletor CMS, as duas melhorias mais proeminentes do coletor G1 são:

(1) Com base no algoritmo mark-collation, não ocorre nenhuma fragmentação de memória.

(2) O tempo de pausa pode ser controlado com muita precisão e a coleta de lixo de baixa pausa pode ser alcançada sem sacrificar o rendimento. O coletor G1 evita a coleta de lixo em toda a área. Ele divide a memória heap em várias áreas independentes de tamanho fixo e rastreia o progresso da coleta de lixo nessas áreas. Ao mesmo tempo, mantém uma lista de prioridades em segundo plano. A cada vez , de acordo com o horário de coleta permitido, as áreas com mais lixo são coletadas primeiro. Divisão de área e mecanismo de reciclagem de área prioritária para garantir que o coletor G1 possa obter a maior eficiência de coleta de lixo em um tempo limitado

51. Mecanismo de carregamento de classe JVM

O mecanismo de carregamento de classe JVM é dividido em cinco partes: carregamento, verificação, preparação, análise e inicialização.Vamos dar uma olhada nesses cinco processos separadamente.

carregar

O carregamento é uma etapa do processo de carregamento da classe, nesta etapa será gerado na memória um objeto java.lang.Class representando esta classe como a entrada de diversos dados desta classe na área de métodos. Observe que não precisa ser obtido de um arquivo Class. Ele pode ser lido de um pacote ZIP (como um pacote jar e um pacote war), ou pode ser calculado e gerado em tempo de execução (proxy dinâmico) ou ele pode ser gerado por outra geração de arquivo (como a conversão de arquivos JSP em classes de classe correspondentes).

verificar

O principal objetivo desta etapa é garantir que as informações contidas no fluxo de bytes do arquivo de classe atendam aos requisitos da máquina virtual atual e não coloquem em risco a segurança da própria máquina virtual.

Preparar

A etapa de preparação é a etapa de alocação formal de memória para variáveis ​​de classe e definição do valor inicial das variáveis ​​de classe, ou seja, alocação do espaço de memória utilizado por essas variáveis ​​na área de método. Preste atenção ao conceito de valor inicial mencionado aqui, por exemplo, uma variável de classe é definida como:

Na verdade, o valor inicial da variável v após o estágio de preparação é 0 em vez de 8080, e a instrução estática put que atribui v a 8080 é armazenada no método construtor de classe após a compilação do programa.

Mas note que se declarado como:

public static final int v = 8080;

O atributo ConstantValue será gerado para v no estágio de compilação e a máquina virtual atribuirá v a 8080 de acordo com o atributo ConstantValue no estágio de preparação.

analisar

A fase de resolução refere-se ao processo no qual a máquina virtual substitui as referências simbólicas no pool de constantes por referências diretas. As referências simbólicas estão no arquivo de classe

public static int v = 8080;

Na verdade, o valor inicial da variável v após o estágio de preparação é 0 em vez de 8080, e a instrução estática put que atribui v a 8080 é armazenada no método construtor de classe após a compilação do programa. Mas note que se declarado como:

O atributo ConstantValue será gerado para v no estágio de compilação e a máquina virtual atribuirá v a 8080 de acordo com o atributo ConstantValue no estágio de preparação.

analisar

A fase de resolução refere-se ao processo no qual a máquina virtual substitui as referências simbólicas no pool de constantes por referências diretas. As referências simbólicas estão no arquivo de classe

public static final int v = 8080;

O atributo ConstantValue será gerado para v no estágio de compilação e a máquina virtual atribuirá v a 8080 de acordo com o atributo ConstantValue no estágio de preparação.

analisar

A fase de resolução refere-se ao processo no qual a máquina virtual substitui as referências simbólicas no pool de constantes por referências diretas. A referência do símbolo está no arquivo de classe:

(1) CONSTANT_Class_info

(2)CONSTANT_Field_info

(3)CONSTANT_Method_info

e outros tipos de constantes.

referência de símbolo

As referências simbólicas não têm nada a ver com o layout da implementação da máquina virtual e os destinos referenciados não precisam necessariamente ser carregados na memória. Os layouts de memória de várias implementações de máquinas virtuais podem ser diferentes, mas as referências simbólicas que podem aceitar devem ser consistentes, porque a forma literal de referências simbólicas é claramente definida no formato de arquivo de classe da Java Virtual Machine Specification.

Citação direta

Uma referência direta pode ser um ponteiro para o destino, um deslocamento relativo ou um identificador que pode localizar indiretamente o destino. Se houver uma referência direta, o destino referenciado já deve existir na memória.

inicialização

A fase de inicialização é a última fase do carregamento de classe. Após a fase de carregamento de classe anterior, exceto para o carregador de classe customizado na fase de carregamento, outras operações são dominadas pela JVM. No estágio inicial, o código do programa Java definido na classe é realmente executado.

construtor de classe

A fase de inicialização é o processo de execução dos métodos construtores de classe. O método é formado combinando a operação de atribuição da variável de classe na classe coletada automaticamente pelo compilador e a instrução no bloco de instrução estática. A máquina virtual garante que o método da classe pai foi executado antes do submétodo ser executado. Se não houver atribuição de variável estática ou bloco de instrução estática em uma classe, o compilador não precisa gerar o método () para isso aula. Observe que a inicialização da classe não será executada nos seguintes casos:

(1) Referenciar os campos estáticos da classe pai por meio da subclasse acionará apenas a inicialização da classe pai, mas não a inicialização da subclasse.

(2) Definir uma matriz de objetos não acionará a inicialização desta classe.

(3) As constantes serão armazenadas no pool de constantes da classe de chamada durante a compilação.Em essência, não há referência direta à classe que define a constante e não acionará a classe onde a constante é definida.

(4) A obtenção do objeto Class por meio do nome da classe não acionará a inicialização da classe.

(5) Ao carregar a classe especificada por meio de Class.forName, se o parâmetro especificado inicializar for false, a inicialização da classe não será acionada.Na verdade, esse parâmetro informa à máquina virtual se deve inicializar a classe.

(6) Por meio do método loadClass padrão do ClassLoader, a ação de inicialização não será acionada.

52. Carregador de classes

A equipe de design da máquina virtual implementa a ação de carregamento fora da JVM para que o aplicativo possa decidir como obter as classes necessárias. A JVM fornece três tipos de carregadores:

Bootstrap ClassLoader

Responsável por carregar as classes no diretório JAVA_HOME\lib, ou no caminho especificado pelo parâmetro -Xbootclasspath, e reconhecidas pela máquina virtual (identificada pelo nome do arquivo, como rt.jar).

Extensão ClassLoader

Responsável por carregar a biblioteca de classes no diretório JAVA_HOME\lib\ext ou no caminho especificado pela variável de sistema java.ext.dirs.

Carregador de classe do aplicativo:

Responsável por carregar bibliotecas de classes no caminho do usuário (classpath). A JVM carrega classes por meio do modelo de delegação pai.Claro, também podemos implementar um carregador de classes personalizado herdando java.lang.ClassLoader.

53. Delegação dos pais

Quando uma classe recebe uma solicitação de carregamento de classe, ela não tentará carregar a própria classe primeiro, mas delegará a solicitação à classe pai para concluir. Esse é o caso de todos os carregadores de classes hierárquicos, portanto, todas as solicitações de carregamento devem ser enviadas ao iniciar carregamento da classe, somente quando o carregador de classes pai relatar que não pode concluir a solicitação (a classe a ser carregada não foi encontrada em seu caminho de carregamento), o carregador de classes filho tentará carregá-la sozinho.

Uma vantagem de usar a delegação parental é que, por exemplo, ao carregar a classe java.lang.Object localizada no pacote rt.jar, não importa qual carregador carregue esta classe, ela será eventualmente confiada ao carregador de classe de inicialização de nível superior para carregamento, o que garante que o uso de diferentes carregadores de classes acabe obtendo o mesmo objeto Object

54. OSGI (Sistema de Modelo Dinâmico)

OSGi (Open Service Gateway Initiative) é um sistema de modelo dinâmico orientado a Java e uma série de especificações para o sistema modular dinâmico de Java.

55. Alterar dinamicamente a estrutura

A plataforma de serviço OSGi oferece a capacidade de alterar dinamicamente as configurações em vários dispositivos de rede sem reinicializar. Para minimizar o acoplamento e torná-los gerenciáveis, a tecnologia OSGi fornece uma arquitetura orientada a serviços que permite que esses componentes se descubram dinamicamente.

56. Programação modular e hot swap

O OSGi visa fornecer as condições básicas para a programação modular de programas Java. Os programas baseados em OSGi provavelmente serão capazes de implementar funções de hot-swap no nível do módulo. Quando o programa é atualizado, apenas parte do programa pode ser desabilitado, reinstalado, e iniciado. , que é um recurso muito atraente para o desenvolvimento de programas corporativos.

OSGi descreve um objetivo de desenvolvimento modular muito bom e define os serviços e a arquitetura necessários para atingir esse objetivo e também possui uma estrutura madura para suporte à implementação. Mas nem todos os aplicativos são adequados para usar OSGi como uma infraestrutura. Embora forneça funções poderosas, também apresenta complexidade adicional porque não segue o modelo de delegação pai de carregamento de classe.

57. Modelo de memória JVM

Exclusivo de encadeamento: pilha, pilha de método nativo, contador de programa

Compartilhamento de thread: heap, área de método

58. pilha

Também conhecido como pilha de métodos, privado para o thread, o método de execução do thread criará uma matriz de pilha para armazenar informações como a tabela de variáveis ​​locais, a pilha de operações, o link dinâmico, a saída do método, etc. Quando o método é chamado , ele é executado na pilha e o método retorna para a pilha. .

59. Pilha de métodos nativos

Semelhante à pilha, também é usado para salvar as informações do método de execução.A execução do método Java usa a pilha, e a pilha do método nativo é usada quando o método Nativo é executado.

60. Contador de programa

Salva a posição do bytecode executado pela thread atual. Cada thread tem um contador independente quando funciona, e serve apenas para executar o método Java. Quando o método Native é executado, o contador do programa fica vazio.

61. Pilha

A maior parte do gerenciamento de memória JVM é compartilhada por threads. A finalidade é armazenar instâncias de objetos. Quase todas as instâncias de objetos desejadas serão colocadas aqui. Quando não houver espaço disponível no heap, uma exceção OOM será lançada. Dependendo do ciclo de vida do objeto, a JVM gerencia os objetos em gerações e o coletor de lixo realiza o gerenciamento da coleta de lixo

62. Área de método

Também conhecida como área não heap, é usada para armazenar dados como informações de classe, constantes, variáveis ​​estáticas e código otimizado pelo compilador de tempo real que foram carregados pela máquina virtual. metaspace de 1.8 são ambos uma implementação da área de método.

63. Reciclagem geracional

A reciclagem geracional é baseada em dois fatos: a maioria dos objetos não será usada em breve e alguns objetos não serão inúteis imediatamente, mas não durarão muito tempo

geração jovem->marcar-cópia

Velha Geração -> Mark-Clear

64. A diferença entre heap e stack

A pilha é uma unidade de tempo de execução, representando a lógica, contendo tipos básicos de dados e referências de objetos na pilha, e a área é contínua sem fragmentação; a pilha é uma unidade de armazenamento, representando dados, que podem ser compartilhados por várias pilhas (incluindo dados básicos tipos em membros , referências e objetos de referência), a área onde eles estão localizados é descontínua e haverá fragmentos.

(1) Diferentes funções

A memória de pilha é usada para armazenar variáveis ​​locais e chamadas de método, enquanto a memória heap é usada para armazenar objetos em Java. Sejam variáveis ​​de membro, variáveis ​​locais ou variáveis ​​de classe, os objetos para os quais eles apontam são armazenados na memória heap.

(2) Compartilhamento diferente

A memória da pilha é privada de thread.

A memória heap é compartilhada por todos os threads.

(3) Erros anormais são diferentes

Se a memória da pilha ou da pilha for insuficiente, uma exceção será lançada.

Espaço de pilha insuficiente: java.lang.StackOverFlowError.

Espaço de heap insuficiente: java.lang.OutOfMemoryError.

(4) Tamanho do espaço

O tamanho da pilha é muito menor que o do heap

65. Quando o FullGC será acionado?

Além de chamar System.gc diretamente, há quatro situações que acionam a execução de Full GC, conforme a seguir.

(1) Espaço insuficiente na geração antiga

O espaço da velha geração será insuficiente apenas quando os objetos da nova geração forem transferidos e criados como objetos grandes e arrays grandes. Quando o espaço ainda for insuficiente após a execução do Full GC, o seguinte erro será lançado

erro:

java.lang.OutOfMemoryError: espaço de heap Java

Para evitar o FullGC causado pelas duas situações acima, ao ajustar, tente deixar o objeto ser reciclado na fase Minor GC, deixe o objeto sobreviver por um período de tempo maior na nova geração e não crie objetos e matrizes.

(2) O espaço de Geração Permanente está cheio

PermanetGeneration armazena algumas informações de classes, etc. Quando houver muitas classes a serem carregadas, classes refletidas e métodos a serem chamados no sistema, o Permanet Generation pode estar cheio.Se não estiver configurado para usar CMS GC, ele executará Full GC. Se ainda não puder ser reciclado após o GC completo, a JVM emitirá a seguinte mensagem de erro:

java.lang.OutOfMemoryError: Espaço PermGen

Para evitar o fenômeno Full GC causado pelo Perm Gen completo, os métodos disponíveis são aumentar o espaço Perm Gen ou mudar para CMS GC.

(3) Falha na promoção e falha no modo simultâneo durante o CMS GC

Para programas que usam CMS para GC de geração antiga, preste atenção especial se houver falha na promoção e falha no modo simultâneo no log do GC. Quando essas duas condições ocorrerem, o GC completo poderá ser acionado.

A falha na promoção é causada pelo fato de que o espaço sobrevivente não pode ser colocado durante o Minor GC, e os objetos só podem ser colocados na geração antiga e, neste momento, a geração antiga não pode ser colocada; a falha no modo simultâneo é causada por objetos a serem colocados em o processo de execução do CMS GC ao mesmo tempo A geração antiga, e neste momento a geração antiga é causada por espaço insuficiente.

As contramedidas são: aumentar o espaço de sobrevivência, espaço de geração antiga ou reduzir a proporção de acionamento de GC simultâneo. No entanto, nas versões JDK 5.0+ e 6.0+, é possível que o CMS acione a ação de varredura muito tempo depois que a observação for concluída devido ao JDK bug29 . Para essa situação, ela pode ser evitada definindo -XX:CMSMaxAbortablePrecleanTime=5 (em ms).

(4) O tamanho médio do CG Menor promovido à Velha Geração é maior que o espaço restante da Velha Geração

Esta é uma situação de gatilho relativamente complicada. Para evitar o fenômeno de espaço insuficiente na geração antiga causado pela promoção de objetos da nova geração para a geração antiga, o Hotspot fez um julgamento ao executar o Minor GC. Se a promoção do Minor GC obtida de as estatísticas anteriores Se o tamanho médio da geração antiga for maior que o espaço restante da geração antiga, o Full GC será acionado diretamente.

Por exemplo, após o programa acionar o MinorGC pela primeira vez, os objetos de 6 MB são promovidos para a geração antiga; então, quando ocorrer o próximo Minor GC, verifique primeiro se o espaço restante da geração antiga é maior que 6 MB e se é menor de 6MB, execute Full GC.

Quando a nova geração adota o PSGC, o método é um pouco diferente. O PS GC também verificará após o Minor GC. Por exemplo, após o primeiro Minor GC no exemplo acima, o PS GC verificará se o espaço restante da geração antiga é maior que 6MB neste momento. , se for menor que, acionará a reciclagem da geração antiga. Além das 4 situações acima, para aplicativos Sun JDK que usam RMI para RPC ou gerenciamento, por padrão, o GC completo será executado uma vez por hora. Você pode definir o intervalo de execução do GC completo passando -java-Dsun.rmi.dgc.client.gcInterval=3600000 na inicialização ou proibir o RMI de chamar System.gc passando -XX:+ DisableExplicitGC

66. O que é uma máquina virtual Java? Por que o Java é chamado de "linguagem de programação independente de plataforma"?

A máquina virtual Java é um processo de máquina virtual que pode executar bytecode Java. Os arquivos de origem Java são compilados em arquivos bytecode que podem ser executados pela máquina virtual Java. Java foi projetado para permitir que os aplicativos sejam executados em qualquer plataforma sem exigir que os programadores reescrevam ou recompilem para cada plataforma individualmente. A máquina virtual Java torna isso possível porque conhece o comprimento da instrução e outras características da plataforma de hardware subjacente.

67. Regras de alocação de objetos

(1) Os objetos são alocados primeiro na área Eden. Se não houver espaço suficiente na área Eden, a máquina virtual executa um Minor GC.

(2) Objetos grandes entram diretamente na velhice (objetos grandes referem-se a objetos que requerem uma grande quantidade de espaço de memória contínuo). O objetivo disso é evitar um grande número de cópias de memória entre a área Eden e as duas áreas Survivor (a nova geração usa o algoritmo de cópia para coletar memória).

(3) Objetos de vida longa entram na velhice. A máquina virtual define um contador de idade para cada objeto. Se o objeto passou pelo Minor GC uma vez, o objeto entrará na área Survivor. Depois disso, a idade do objeto aumentará em 1 cada vez que ele passar pelo Minor GC. Até o limite é atingido, o objeto entrará na área de velhice.

(4) Julgue dinamicamente a idade do objeto. Se a soma dos tamanhos de todos os objetos da mesma idade na área do Sobrevivente for maior que a metade do espaço do Sobrevivente, os objetos cuja idade é maior ou igual a esta idade podem entrar diretamente na velhice.

(5) Garantia de alocação de espaço. Toda vez que um Minor GC for executado, a JVM calculará o tamanho médio dos objetos movidos da área Survivor para a área antiga. Se esse valor for maior que o tamanho do valor restante na área antiga, um Full GC será executado. Se for menor que o valor, verifique a configuração HandlePromotionFailure. Se for verdadeiro, apenas o Monitor será executado. GC, se falso, execute o GC completo

68. Descreva o mecanismo principal dos arquivos de classe de carregamento da JVM?

O carregamento de classes na JVM é implementado pelo carregador de classes (ClassLoader) e suas subclasses.O carregador de classes em Java é um importante componente do sistema de tempo de execução Java, responsável por localizar e carregar arquivos de classe em tempo de execução.

Devido à natureza de plataforma cruzada do Java, o programa de origem Java compilado não é um programa executável, mas um ou mais arquivos de classe. Quando um programa Java precisa usar uma determinada classe, a JVM garantirá que a classe foi carregada, conectada (validada, preparada e analisada) e inicializada.

Carregamento de classe refere-se à leitura dos dados no arquivo .class da classe na memória, geralmente criando uma matriz de bytes e lendo-a no arquivo .class e, em seguida, gerando um objeto Class correspondente à classe carregada. Após a conclusão do carregamento, o objeto Class não está completo, portanto, a classe neste momento ainda não está disponível.

Quando a classe é carregada, ela entra na fase de conexão, que inclui três etapas: verificação, preparação (alocar memória para variáveis ​​estáticas e definir valores iniciais padrão) e resolução (substituir referências simbólicas por referências diretas). Finalmente, a JVM inicializa a classe,

incluir:

(1) Se a classe tiver uma classe pai direta e a classe não tiver sido inicializada, inicialize a classe pai primeiro;

(2) Se houver instruções de inicialização na classe, execute essas instruções de inicialização em sequência. O carregamento de classe é feito por carregadores de classe, que incluem: carregador raiz (BootStrap), carregador de extensão (Extension), carregador de sistema (System) e carregador de classe definido pelo usuário (subclasse de java.lang.ClassLoader).

A partir do Java 2 (JDK 1.2), o processo de carregamento de classe adota o mecanismo de delegação pai (PDM). O PDM garante melhor a segurança da plataforma Java, neste mecanismo o Bootstrap que vem com a JVM é o root loader, e os demais loaders possuem e possuem apenas um class loader pai. O carregamento da classe primeiro solicita que o carregador de classes pai carregue e, quando o carregador de classes pai estiver indefeso, seu carregador de classes filho o carregará sozinho. A JVM não fornece referências ao Bootstrap para programas Java. A seguir está uma descrição de vários carregadores de classe

Bootstrap: geralmente implementado com código local, responsável por carregar a biblioteca de classes do núcleo básico da JVM (rt.jar);

Extensão: Carregue a biblioteca de classes do diretório especificado pela propriedade de sistema java.ext.dirs e seu carregador pai é Bootstrap;

Sistema: Também conhecido como o carregador de classes do aplicativo, sua classe pai é Extension. É o carregador de classes mais amplamente utilizado. Ele registra classes do diretório especificado pela variável de ambiente classpath ou pela propriedade do sistema java.class.path e é o carregador pai padrão para carregadores definidos pelo usuário.

69. Processo de criação de objetos Java

(1) Quando a JVM encontra uma instrução para criar um novo objeto, ela primeiro verifica se os parâmetros dessa instrução podem definir uma referência simbólica a uma classe no pool de constantes. Em seguida, carregue esta classe (o processo de carregamento da classe será discutido posteriormente)

(2) Alocar memória para o objeto. Um método "colisão de ponteiro", um método "lista livre" e, finalmente, o método comumente usado "alocação de buffer de thread local (TLAB)"

(3) Inicialize o espaço de memória do objeto, exceto o cabeçalho do objeto para 0

(4) Faça as configurações necessárias para o cabeçalho do objeto

70. Descreva resumidamente a estrutura de objeto de Java

Um objeto Java consiste em três partes: cabeçalho do objeto, dados da instância e preenchimento de alinhamento.

O cabeçalho do objeto consiste em duas partes: a primeira parte armazena os dados de tempo de execução do próprio objeto: código hash, idade de geração do GC, status de identificação do bloqueio, bloqueio mantido pelo thread e ID do thread tendencioso (geralmente ocupando 32/64 bits). A segunda parte é o tipo de ponteiro, que aponta para o tipo de metadados da classe do objeto (ou seja, qual classe o objeto representa). Se for um objeto array, outra parte do cabeçalho do objeto é usada para registrar o comprimento do array.

Os dados da instância são usados ​​para armazenar as informações efetivas reais do objeto (incluindo herdadas da classe pai e definidas por si mesmas)

Preenchimento de alinhamento: a JVM requer que o endereço inicial do objeto seja um número inteiro múltiplo de 8 bytes (alinhamento de 8 bytes)

71. Como julgar se um objeto pode ser reciclado

Geralmente, existem duas maneiras de determinar se um objeto está vivo:

Contagem de referência: Cada objeto possui um atributo de contagem de referência.Quando uma referência é adicionada, a contagem é incrementada em 1, e quando a referência é liberada, a contagem é decrementada em 1. Quando a contagem é 0, ela pode ser reciclada. Este método é simples e não pode resolver o problema de referências circulares entre objetos.

Análise de acessibilidade: Partindo das raízes do GC para pesquisar para baixo, o caminho percorrido pela pesquisa é chamado de cadeia de referência. Quando um objeto não possui nenhuma cadeia de referência conectada ao GC Roots, isso prova que o objeto está indisponível e inacessível.

72. A coleta de lixo ocorrerá na geração permanente da JVM?

A coleta de lixo não acontece na geração permanente. Se a geração permanente estiver cheia ou exceder um limite, uma coleta de lixo completa (Full GC) será acionada. Se você observar cuidadosamente a saída do coletor de lixo, verá que a geração permanente também é coletada. É por isso que o tamanho correto da geração permanente é muito importante para evitar Full GC. Consulte Java8: Da geração permanente à área de metadados (Nota: Java8 removeu a geração permanente e adicionou uma nova área de memória nativa chamada área de metadados)

73. Algoritmo de coleta de lixo

Existem três algoritmos básicos para GC: algoritmo de varredura de marca, algoritmo de cópia e algoritmo de compactação de marca.Nossos coletores de lixo comumente usados ​​geralmente usam algoritmos de coleta geracional.

algoritmo de varredura de marcação

O algoritmo "Mark-Sweep", como seu nome, é dividido em duas etapas: "Mark" e "Sweep".objeto marcado.

algoritmo de cópia

Algoritmo de coleta de "cópia", que divide a memória disponível em duas partes de tamanho igual de acordo com a capacidade e usa apenas uma delas por vez. Quando a memória deste bloco for usada, copie o objeto sobrevivente para outro bloco e, em seguida, limpe o espaço de memória usado de uma só vez.

algoritmo de compressão de marca

O processo de marcação ainda é o mesmo que o algoritmo "mark-clear", mas as etapas subsequentes não limpam diretamente os objetos recicláveis, mas permitem que todos os objetos sobreviventes se movam para uma extremidade e, em seguida, limpem diretamente a memória fora do limite final

Algoritmo de coleta geracional

O algoritmo "Coleta Geracional" divide o heap Java em nova geração e velha geração, para que seja adotado o algoritmo de coleta mais adequado de acordo com as características de cada idade

74. Quais são os comandos de afinação?

Os comandos de monitoramento e tratamento de falhas do Sun JDK incluem jps jstat jmap jhat jstack jinfo

(1) jps, JVM Process Status Tool, exibe todos os processos de máquina virtual HotSpot no sistema especificado.

(1) jstat, monitoramento de estatísticas JVM é um comando usado para monitorar as informações de status da máquina virtual quando ela está em execução. Ele pode exibir os dados em execução, como carregamento de classe, memória, coleta de lixo e compilação JIT no processo da máquina virtual .

(3) jmap, o comando JVM Memory Map é usado para gerar arquivos de despejo de heap

(4) jhat, o comando JVM Heap Analysis Tool é usado em conjunto com jmap para analisar o despejo gerado pelo jmap. jhat tem um servidor HTTP/HTML em miniatura integrado. Depois de gerar os resultados da análise de despejo, você pode visualizá-los no navegador

(5) jstack, usado para gerar um instantâneo da thread da máquina virtual java no momento atual.

(6) jinfo, JVM Configuration info Este comando é usado para visualizar e ajustar os parâmetros operacionais da máquina virtual em tempo real

75. Ferramentas de ajuste

As ferramentas de ajuste comumente usadas são divididas em duas categorias, o jdk vem com ferramentas de monitoramento: jconsole e jvisualvm, e terceiros incluem: MAT (Memory Analyzer Tool), GChisto.

(1) jconsole, Java Monitoring and Management Console é um console de gerenciamento e monitoramento java que vem com o JDK a partir do java5 e é usado para monitorar memória, threads e classes na JVM

(2) jvisualvm, jdk vem com uma ferramenta versátil que pode analisar instantâneos de memória e instantâneos de encadeamento; monitorar mudanças de memória, mudanças de GC, etc.

(3) MAT, Memory Analyzer Tool, uma ferramenta de análise de memória baseada no Eclipse, é uma ferramenta de análise Javaheap rápida e rica em recursos, que pode nos ajudar a encontrar vazamentos de memória e reduzir o consumo de memória

(4) GChisto, uma ferramenta profissional para analisar logs gc

76. Quando ocorre a GC menor e a GC completa, respectivamente?

O MGC, também chamado de YGC, ocorre quando a memória da nova geração é insuficiente, e o FGC ocorre quando a memória da JVM é insuficiente.

77. Qual ajuste de desempenho da JVM você conhece?

Definir o tamanho da memória heap

-Xmx: Limite máximo de memória heap.

Defina o tamanho da geração jovem. A nova geração não deve ser muito pequena, caso contrário, um grande número de objetos irá inundar a velha geração

-XX:NewSize: tamanho da nova geração

-XX:NewRatio A proporção da nova geração e da velha geração

-XX:SurvivorRatio: a proporção entre o espaço do Éden e o espaço do sobrevivente

Definir coletor de lixo geração jovem -XX:+UseParNewGC geração antiga -XX:+UseConcMarkSweepGC

afinal

Bem-vindo a prestar atenção à conta oficial: programador perseguindo o vento, responda 003 para receber o último manual de perguntas da entrevista Java 2020 (mais de 200 páginas de resumo em PDF)

 

Acho que você gosta

Origin blog.csdn.net/Design407/article/details/106874914
Recomendado
Clasificación