Al ubicar los problemas de rendimiento de JVM, puede encontrar fugas de memoria y causar JVM fuera de Memory. Si el parámetro Reloadable = "True" se establece cuando se usa el contenedor TomCat, también puede encontrar el desbordamiento de la memoria al implementar aplicaciones con frecuencia. El principio de implementación en caliente de TomCat es detectar que los archivos en el directorio web-INF/classes o Web-INF/LIB se cambian y la aplicación se detendrá primero y luego comenzará. Dado que TomCat asigna un WebAppClassLoader a cada aplicación de forma predeterminada, el principio de reemplazo en caliente es crear un nuevo cargador de clases para cargar la clase. Dado que la singularidad de una clase en el JVM está determinada por su archivo de clase y su cargador de clase, recargar la clase puede lograr el propósito del reemplazo en caliente. Cuando el número de implementaciones en caliente es más frecuente, causará más clases cargadas por el JVM. Si la clase anterior no se descarga a tiempo por alguna razón (como fugas de memoria), puede conducir a una generación permanente o a MetaSpace OutofMemory. Este artículo presenta brevemente el escenario donde ThreadLocal y ClassLoader conducen a fugas de memoria y, en última instancia, a través de una demostración a través de una demostración.
Desinstalar la clase
Después de que se usa la clase, si se satisface la siguiente situación, se desinstalará:
1. Todas las instancias de esta clase en el montón se han reciclado, es decir, los objetos de instancia de esta clase no existen en el montón.
2. El cargador de clases que carga esta clase ha sido reciclado.
3. El objeto de clase correspondiente a esta clase no se puede hacer referencia a ninguna parte, y no se puede acceder al objeto de clase a través de la reflexión.
Si la clase cumple con las condiciones de desinstalación, el JVM desinstala la clase cuando está en GC, es decir, borra la información de la clase en el área de método.
Introducción de la escena
En el artículo anterior, presenté el principio de ThreadLocal. Cada hilo tiene un ThreadLocalMap. Si el ciclo de vida del hilo es relativamente largo, la entrada en ThreadLocalMap puede no ser reciclada. El objeto Threadlocal siempre ha sido fuertemente referido por el hilo. Dado que el objeto de instancia mantendrá la referencia del objeto de clase, el objeto de clase contendrá la referencia del cargador de clases que lo cargue, lo que hará que la clase se descargue. Cuando hay suficientes clases cargadas, puede ocurrir una generación permanente o desbordamiento de la memoria de Metaspace. Si la clase tiene objetos grandes, como una matriz de bytes más grande, causará el desbordamiento de la memoria del área de montón Java.
Introducción del código fuente
Aquí hay una clase interna interna. La clase interna tiene un objeto static hiltlocal, que se usa principalmente para hacer que los hilos tengan referencias fuertes a la clase interna, de modo que la clase interna no se puede reciclar. Se define un cargador de clase personalizado para cargar la clase interna, como se muestra a continuación:
public class MemoryLeak {public static void main (string [] args) {// Debido a que el hilo se ejecuta todo el tiempo, el objeto interno en ThreadLocalMap ha sido fuertemente referenciado por hilo de hilo nuevo hilo (new runnable () {@Override public public void run () {while (true) {// cada vez que se crea una nueva instancia de clase de clases de clases de clase inner. ("Load1", MemoryLeak.Class.getClassLoader (), "COM.EZLIPPI.MEMORYLEAK $ GC para el procesamiento de referencia InnClass = NULL; } // Para llegar al área de almacenamiento de montón más rápida, la clase estática interna {byte privado [] mb = new Byte [1024 * 1024]; Static ThreadLocal <NoNner> ThreadLocal = new ThreadLocal <Nner> () {@Override Inner InitialValue () {return new Inner (); }}; // para llamar a ThreadLocal.get () para inicializar un objeto interno Static {ThreadLocal.get (); } public inner () {}} // código fuente omitir la clase estática privada CustomClassLoader extiende ClassLoader {}Desbordamiento de la memoria del área del montón
Para activar el desbordamiento de la memoria del montón, configuré una matriz de bytes de 1 MB en la clase interna, y al mismo tiempo, tengo que llamar a ThreadLocal.get () en el bloque estático. Solo la llamada activará InitialValue () para inicializar un objeto interno, de lo contrario, simplemente crearé un objeto de hilo vacío, y no hay datos en ThreadLocalMap.
Los parámetros JVM son los siguientes:
-Xms100m -xmx100m -xx:+useparnewgc -xx:+useconcmarksweepgc -xx:+printgcdetails -xx:+printheapatgc -xx:+printclasshistogram -xx:+heapdumponoutofmoryerrorRor
Después de las últimas 814 ejecuciones, la memoria del área del montón JVM se desborda, como se muestra a continuación:
java.lang.OUTOFMemoryError: Java Heap Spacedumping Heap a Java_pid11824.hprof ... archivo de volcado de montón creado [100661202 bytes en 1.501 segundos] Heap par New Generation Total 30720k, usado 30389k [0x000000000f9c0000000000000000000000FBD50000000) EDEN EDEN 99% usado [0x0000000000F9C000000, 0x0000000000FB6AD450, 0X0000000000000000FB6B0000) Desde el espacio 3392k, 90% usado [0x00000000000000FB6B0000, 0x0000000000FB9B0030, 0x0000000000FBA00000) AL ESPACIO 3392K, 0% UTILI [0x000000000000FBA00000, 0x0000000000FBD500000) Generación concurrente de la marca Total 68288k, usado 67600k [0x000000000FBD500000, 0x00000010000000000000, 0x00000001000000000) MetaSpace usado 3770K, capacidad 5134K, 5248k, se reservó 10567676767610) Espacio de clase utilizado 474k, capacidad 578k, comprometido 640k, reservado 1048576KEXCEPTERCION en hilo "hilo-0" java.lang.oUtofMemoryError: espacio de montón java en com.ezlippi.Memoryleak $ inner. <stinit> (memoria de memoria.Java:34) AT sun.reflect.nativeconstructorAccessorImpl.newinstance0 (método nativo) en sun.reflect.nativeconstructorAccessorImpl.newinstance (fuente desconocida) en Sun.reflect.DelegatingConstructorAccessorM.Newinstance (Fuente desconocida) en Java.lang.reflect.Constructor.neweNewinSpplance) java.lang.reflect.constructor.newinstance (fuente desconocida) Fuente) en java.lang.class.newinstance (fuente desconocida) en com.ezlippi.Memoryleak $ 1.run (MemoryLeak.Java:20) en Java.lang.thread.run (Fuente desconocida)
Puede ver que el JVM no tiene memoria para crear un nuevo objeto interno, porque el área de Heap almacena muchas matrices de bytes de 1 MB. Aquí imprimí el histograma de la clase (la siguiente figura es una escena con un tamaño de montón de 1024m), omitiendo algunas clases insignificantes. Se puede ver que la matriz de bytes ocupa 855m de espacio, y 814 instancias de com.ezlippi.Memoryleak $ CustomClassLoader se crean, que básicamente coinciden con el tamaño de la matriz de bytes:
num #instances #bytes clase nombre------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------- 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.hapcharbuffer 19: 817 32680 java.security 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 $ Threadlocalmap $ Entrada
Desbordamiento de Metaspace
Para hacer desbordamiento de Metaspace, debe reducir un poco el espacio de Metaspace y cargar suficientes clases antes del desbordamiento del montón. Por lo tanto, ajusté los parámetros JVM y ajusté el tamaño de la matriz de bytes a 1KB, como se muestra a continuación:
byte privado [] kb = nuevo byte [1024]; -xms100m -xmx100m -xx:+useParnewgc -xx:+useConcmarkSweepgc -xx:+printgcdetails -xx:+printhapatgc -xx:+impreClasshistrogram -xx: MetaSpacesize = 2m -xx: maxxx: maxxx: maxmet = 2mmetAspacesspacessy
Desde el registro de GC, podemos ver que cuando el MetoSpace alcance el umbral de GC (es decir, el tamaño de la configuración MaxTaSpacesize) se activará FULLGC:
java.lang.OutofMemoryError: Metaspace << no hay traza de pila disponible >> {Heap antes de GC Invocations = 20 (Full 20): Par New Generation Total 30720k, usado 0K [0x000000000F9C00000000, 0x0000000000000000000000000000000) EDEN 27328K, 0% usado [0x0000000f9c9c00000000000000, 0x00000000F9C00000, 0x0000000F9C00000, 0x0000000F9C00000, 0x0000000FB6B0000) desde el espacio 3392k, 0% utilizado [0x000000000000FB6B0000, 0x000000000000000000000000000000000000000000FBA00000) AL ESPACIO 3392K, 0% UTILIZAN [0x0000000000fba00000, 0x000000000fbd500000) to space 3392K, 0% used [0x0000000000fba00000, 0x000000010000000000) Metaspace used 1806K, capacity 1988K, committed 2048K, reserved 1056768K class space used 202K, capacity 384K, committed 384K, reserved 1048576k [GC completo (umbral de metadatos GC) [CMSProcess terminado con el código de salida 1Del ejemplo anterior, podemos ver que si el cargador de clase y Threadlocal se usan de manera incorrecta, de hecho conducirá a una fuga de memoria. El código fuente completo está en GitHub