Ao localizar problemas de desempenho da JVM, você pode encontrar vazamentos de memória e causar a JVM OutfMemory. Se o parâmetro Reloadable = "true" estiver definido ao usar o contêiner Tomcat, você também poderá encontrar o excesso de memória ao implantar frequentemente aplicativos. O princípio da implantação quente do TomCat é detectar que os arquivos no diretório Web-Inf/Classes ou Web-Inf/Lib são alterados e o aplicativo será interrompido primeiro e depois iniciado. Como o TomCat atribui um WebAppClassLoader a cada aplicativo por padrão, o princípio da substituição a quente é criar um novo carregador de classe para carregar a classe. Como a singularidade de uma classe na JVM é determinada por seu arquivo de classe e seu carregador de classe, a recarga da classe pode alcançar o objetivo da substituição a quente. Quando o número de implantações quentes for mais frequente, isso causará mais classes carregadas pela JVM. Se a classe anterior não for descarregada a tempo por algum motivo (como vazamentos de memória), ela poderá levar a geração permanente ou ao Metaspace Outfemory. Este artigo apresenta brevemente o cenário em que o ThreadLocal e o Classloader levam a vazamentos de memória e, finalmente, superam a memória por meio de uma demonstração.
Desinstalar a classe
Depois que a aula for usada, se a seguinte situação for satisfeita, ela será desinstalada:
1. Todas as instâncias dessa classe na pilha foram recicladas, ou seja, os objetos de instância dessa classe não existem na pilha.
2. O carregador de classe esta classe foi reciclado.
3. O objeto de classe correspondente a esta classe não pode ser referenciado em qualquer lugar e o objeto de classe não pode ser acessado através da reflexão.
Se a classe atender às condições de desinstalação, a JVM desinstala a classe quando está no GC, ou seja, limpa as informações da classe na área do método.
Introdução à cena
No artigo anterior, introduzi o princípio do Threadlocal. Cada thread possui um threadlocalmap. Se o ciclo de vida do fio for relativamente longo, a entrada no Threadlocalmap pode não ser reciclada. O objeto Threadlocal sempre foi fortemente referenciado pelo thread. Como o objeto de instância manterá a referência do objeto de classe, o objeto de classe manterá a referência do carregador de classe que o carrega, o que fará com que a classe seja descarregada. Quando há classes suficientes carregadas, pode ocorrer uma geração permanente ou o excesso de memória da Metaspace. Se a classe tiver objetos grandes, como uma matriz de bytes maior, causará o transbordamento de memória da área de heap java.
Introdução ao código -fonte
Aqui está uma classe interna interna. A classe interna possui um objeto estático de threadlocal, que é usado principalmente para fazer com que os threads mantenham fortes referências à classe interna, para que a classe interna não possa ser reciclada. Um carregador de classe personalizado é definido para carregar a classe interna, como mostrado abaixo:
public class MemoryLeak {public static void main (string [] args) {// Como o thread está funcionando o tempo todo, o objeto interno no threadlocalmap foi fortemente referenciado pelo objeto Thread New Thread (new Runnable () {@Override public void run () {while) ("Load1", MemoryLeak.class.getclassload (), "com.ezlippi.memoryleak $ interno", "com.ezlippi.memoryleak $ interno $ 1"); GC para processamento de referência INERCLASS = NULL; } // Para atingir a área de heap mais rápida classe estática pública interna {private byte [] mb = novo byte [1024 * 1024]; estático threadlocal <iner> threadlocal = new ThreadLocal <iner> () {@Override protegido Initer InitialValue () {return New Inner (); }}; // para chamar Threadlocal.get () para inicializar um objeto interno estático {threadlocal.get (); } public interno () {}} // Código fonte omitir classe estática privadaMemória da área da pilha transbordamento
Para acionar o excesso de memória da heap, configurei uma matriz de 1 MB de bytes na classe interna e, ao mesmo tempo, tenho que chamar Threadlocal.get () no bloco estático. Somente a chamada desencadeará o InitialValue () para inicializar um objeto interno; caso contrário, apenas criarei um objeto Threadlocal vazio e não há dados no Threadlocalmap.
Os parâmetros da JVM são os seguintes:
-Xms100m -xmx100m -xx:+useParnewgc -xx:+useConcmarksweepgc -xx:+printgcdetails -xx:+imprimtheapatgc -xx:+printClassHistogram -xx:+heapdiponoutOfMemoryError
Após as últimas 814 execuções, a memória da área de heap JVM transborda, como mostrado abaixo:
java.lang.outofMemoryError: Java Heap Spacacedumping Heap para java_pid11824.hprof ... arquivo de despejo de pesos criado [100661202 bytes em 1,501 seg. 99% utilizado [0x00000000F9C00000000, 0x00000000FB6AD450, 0x00000000FB6B0000) do espaço 3392K, 90% usado [0x0000000000FB6B0000, 0x000000000000FB9990, 0x00000000FBAB6B0000) a SpaceB999999990000000000000001B6B0000) a SpaceB9999990000000000010100000101010101010100000000010100000000010. [0x0000000000FBA00000, 0x00000000FBD500000) Total de geração de varredura de marca simultânea 68288K, usada 67600K [0x000000000FBD500000, 0x0000001000000.000, 0x0000000100000) Metaspace 3770K, CAPACIONAL Espaço de aula usado 474K, capacidade 578K, comprometido com 640k, reservado 1048576 Kexception no thread "Thread-0" java.lang.outOfMemoryError: Java Heap Space em com.ezlippi.memoryleak $. sun.reflelect.nativeConstructorAccessorImpl.NewInstance0 (método nativo) em Sun.reflelect.nativeConstructorAccessorImpl.NewInstance (fonte desconhecida) em sun.reflect.DelegatingConstructRuctROccess.NewInstance (Unknow Sound) em java. java.lang.reflect.constructor.newinstance (fonte desconhecida) fonte) em java.lang.class.newinstance (fonte desconhecida) em com.ezlippi.memoryleak $ 1.run (relnkleak.java:20) em java.lang.thread.run (desconhecido)
Você pode ver que a JVM não tem memória para criar um novo objeto interno, porque a área de heap armazena muitas matrizes de 1 MB de bytes. Aqui, imprimi o histograma da classe (a figura a seguir é uma cena com um tamanho de pó de 1024m), omitindo algumas classes insignificantes. Pode -se observar que a matriz de bytes ocupa 855m de espaço e 814 instâncias de com.ezlippi.memoryleak $ customClassLoader são criados, que basicamente correspondem ao tamanho da matriz de bytes:
NUM #Instances #Bytes Class nome------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- com.ezlippi.memoryleak $ CustomClassLoader 12: 820 53088 [ljava.util.hashtable $ entrada; 15: 817 39216 java.util.hashtable 16: 915 36600 java.lang.ref.softreference 17: 543 34752 Java.Net.url 18: 697 33456 Java.nio.heapcharbuffer 19: 817 3288880 Java.util.treemap $ Entrada 21: 928 29696 Java.util.HashTable $ Entrada 22: 1802 28832 java.util.HashSet 23: 817 26144 Java.Security.Codesource 24: 814 26048 JavA.Lang.ThreadLocal.ThreadLocal.Codesource 24: 814 26048 JavA.Lang.Threadlocal
Metaspace Overflow
Para criar um excesso de metapacidade, você deve reduzir um pouco o espaço do metapacial e carregar classes suficientes antes do transbordamento da pilha. Portanto, ajustei os parâmetros da JVM e ajustei o tamanho da matriz de bytes para 1kb, como mostrado abaixo:
byte privado [] kb = novo byte [1024]; -xms100m -xmx100m -xx:+useParnewgc -xx:+useconCmarksweepgc -xx:+printgcdetails -xx:+prostTheApatc -xx:+PrintClASSHISTOMATROMATOS -XX:+PRINTHEAPATG -XX:+
Do log do GC, podemos ver que, quando o Meraspacy atingir o limite do GC (ou seja, o tamanho da configuração maxmetaspacesize) fullGC será acionado:
java.lang.outofMemoryError: metaspace << Nenhum rastreamento de pilha disponível >> {heap antes de invocações do GC = 20 (completa 20): PAR New Generation Total 30720k, usado 0k [0x000000000F9C000000, 0x00000000000000000000). 0x00000000f9c00000, 0x0000000f9c00000, 0x0000000f9c00000, 0x0000000fb6b0000) from space 3392K, 0% used [0x0000000000fb6b0000, 0x00000000fb6b0000, 0x00000000fba00000) to space 3392K, 0% used [0x0000000000FBA00000, 0x000000000FBD500000) para o espaço 3392K, 0% usado [0x0000000000FBA00000, 0x000000010000000000) Metaspacy Utilizado 1806k, capacidade 1988k, comprometido 2048k, referido 105678K Space 202ks 202, Capacidade 1988, 2048k, referido 105678K USTUNHADO 202, SPACIONCAKE DO CASAKK, 1988K, 2048K, referido 105678ks a classificação de Class8K, 1988k, 2048k, com capacidade para 2048k, com capacidade para 2048k, com capacidade para 2048k, com capacidade para 2048k, com capacidade para 2048k, com capacidade para 2048k, com capacidade para 2048k, com capacidade para 2048k. 1048576k [GC completo (limiar de metadados gc) [CMSProcess terminado com o código de saída 1A partir do exemplo acima, podemos ver que, se o carregador de classe e o threadlocal forem usados incorretamente, ele realmente levará ao vazamento de memória. O código -fonte completo está no github