Tecnología de análisis de escape de Java

Análisis de escape

¿Qué es el escape?

Escapar se refiere a un objeto creado dentro de un método, además de ser referenciado dentro del cuerpo del método, también es referenciado por otras variables fuera del cuerpo del método; la consecuencia de esto es que después de que se ejecuta el método, el objeto creado en el El método no puede ser reciclado por el GC porque es referenciado por otras variables. En una llamada de método normal, el objeto creado en el cuerpo del método se reciclará después de que se complete la ejecución; por lo tanto, debido a que no se puede reciclar, se convierte en un escape.

En el sistema de compilación de Java, el proceso de convertir un archivo de código fuente de Java en una instrucción de máquina ejecutable por computadora requiere dos etapas de compilación:

         El primer párrafo: es convertir el archivo .java en un archivo .class.

         Segundo párrafo: La compilación es el proceso de convertir .class en instrucciones de máquina.

La primera compilación es el comando javac

En la segunda etapa de compilación, JVM interpreta los códigos de bytes para traducirlos en las instrucciones de la máquina correspondientes, lee uno por uno e interpreta la traducción uno por uno. Obviamente, después de la interpretación y ejecución, su velocidad de ejecución será inevitablemente mucho más lenta que la de los programas ejecutables de código binario. Esta es la función del intérprete JVM tradicional. Para resolver este problema de eficiencia, se introdujo la tecnología JIT (Just-in-Time Compilation).

Después de la introducción de la tecnología JIT, los programas Java todavía se interpretan y ejecutan a través de un intérprete.Cuando la JVM encuentra que un método o bloque de código se ejecuta con mucha frecuencia, lo considerará un "Código de punto caliente" (Hot Spot Code). Luego, JIT traducirá parte del "código activo" al código de máquina relacionado con la máquina local, lo optimizará y luego almacenará en caché el código de máquina traducido para el próximo uso.

Debido al contenido de la compilación JIT y la detección de puntos calientes, ya lo he introducido en el análisis en profundidad de los principios de compilación de Java, por lo que no los repetiré aquí. Este artículo presenta principalmente la optimización en JIT. El más importante en la optimización JIT es el análisis de escape.

Análisis de escape

Con respecto al concepto de análisis de escape, puede consultar el artículo que dice que los objetos no están necesariamente todos asignados en el montón. Aquí hay una breve revisión:

El comportamiento básico del análisis de escape es analizar el alcance dinámico de un objeto : cuando un objeto se define en un método, puede ser referenciado por un método externo, como pasar a otros lugares como un parámetro de llamada, que se llama método escapar.

Por ejemplo, el siguiente código:

public static StringBuffer craeteStringBuffer(String s1, String s2) {
    StringBuffer sb = new StringBuffer();
    sb.append(s1);
    sb.append(s2);
    return sb;
}
public static String createStringBuffer(String s1, String s2) {
    StringBuffer sb = new StringBuffer();
    sb.append(s1);
    sb.append(s2);
    return sb.toString();
}

El sb en el primer fragmento de código escapa, pero el sb en el segundo fragmento de código no escapa.

Mediante el análisis de escape, el compilador puede optimizar el código de la siguiente manera:

1. Omisión sincrónica.

Si se encuentra que un objeto es accesible solo desde un subproceso, entonces se puede ignorar la operación de este objeto.

2. Convierta la asignación de pila en asignación de pila.

Si se asigna un objeto en una subrutina, de modo que el puntero al objeto nunca se escape, el objeto puede ser un candidato para la asignación de pila en lugar de la asignación de montón.

3. Separación de objetos o sustitución escalar.

Es posible que algunos objetos no necesiten existir como una estructura de memoria continua para acceder, por lo que es posible que parte (o todo) del objeto no se almacene en la memoria, sino que se almacene en el registro de la CPU.

Cuando el código Java se está ejecutando, puede especificar si habilitar el análisis de escape a través del parámetro JVM,

-XX: + DoEscapeAnalysis: Indica que se abra el análisis de escape

-XX: -DoEscapeAnalysis: indica que el análisis de escape se ha desactivado de forma predeterminada desde jdk 1.7. Si necesita desactivarlo, debe especificar -XX: -DoEscapeAnalysis

Cuatro, omisión de sincronización

Al compilar dinámicamente el bloque de sincronización, el compilador JIT puede usar el análisis de escape para determinar si el objeto de bloqueo utilizado por el bloque de sincronización solo puede ser accedido por un subproceso y no liberado a otros subprocesos.

Si el objeto de bloqueo utilizado por el bloque de sincronización solo puede ser accedido por un hilo a través de este análisis, el compilador JIT cancelará la sincronización de esta parte del código al compilar el bloque de sincronización. Este proceso de cancelar la sincronización se denomina omisión de sincronización o eliminación de bloqueo .

Como el siguiente código:

public void f() {
    Object hollis = new Object();
    synchronized(hollis) {
        System.out.println(hollis);
    }
}

El objeto hollis está bloqueado en el código, pero el ciclo de vida del objeto hollis está solo en el método f () y no será accedido por otros subprocesos, por lo que se optimizará durante la fase de compilación JIT. Optimizado en:

public void f() {
    Object hollis = new Object();
    System.out.println(hollis);
}

Por lo tanto, cuando se usa sincronizado, si JIT encuentra que no hay un problema de seguridad de subprocesos después del análisis de escape, eliminará el bloqueo.

Sustitución escalar

Escalar se refiere a datos que no se pueden dividir en datos más pequeños. El tipo de datos primitivo en Java es un escalar. Por el contrario, los datos que se pueden descomponer se denominan Agregados. Los objetos en Java son agregados, porque se pueden descomponer en otros agregados y escalares.

En la etapa JIT, si se descubre que el mundo exterior no puede acceder a un objeto después del análisis de escape, luego de la optimización JIT, el objeto se desensamblará en varias variables miembro contenidas en él. Este proceso es un reemplazo escalar.

public static void main(String[] args) {
   alloc();
}
private static void alloc() {
   Point point = new Point(1,2);
   System.out.println("point.x="+point.x+"; point.y="+point.y);
}
class Point{
    private int x;
    private int y;
}

En el código anterior, el objeto de punto no escapa del método alloc, y el objeto de punto se puede desmontar en escalares. Entonces, JIT no creará directamente un objeto Point, sino que usará directamente dos escalares int x e int y para reemplazar el objeto Point.

El código anterior, después del reemplazo escalar, se convertirá en:

private static void alloc() {
   int x = 1;
   int y = 2;
   System.out.println("point.x="+x+"; point.y="+y);
}

Se puede observar que luego del análisis de escape de Point, se encontró la cantidad de agregación que no escapó, se reemplazó con dos cantidades de agregación. Entonces, ¿cuáles son los beneficios de la sustitución escalar?

La ventaja del reemplazo escalar es que puede reducir en gran medida el uso de la memoria del montón. Porque una vez que no es necesario crear un objeto, ya no es necesario asignar memoria de pila.

La sustitución escalar proporciona una buena base para la asignación en la pila.

Asignación en la pila

En la máquina virtual Java, a los objetos se les asigna memoria en el montón de Java, lo cual es de sentido común. Sin embargo, hay un caso especial, es decir, si después del análisis de escape se descubre que un objeto no tiene un método de escape, entonces puede optimizarse para ser asignado en la pila. De esta forma, no hay necesidad de asignar memoria en el montón y no hay necesidad de recolectar basura.

Para obtener una introducción detallada a la asignación en la pila, puede consultar que el objeto no es necesariamente toda la memoria asignada en el montón

Aquí, todavía quiero decir brevemente, de hecho, en la máquina virtual existente, no hay una implementación real en la pila. En nuestro ejemplo, el objeto no está en el montón. En nuestro ejemplo, el objeto no está necesariamente todo asignado En realidad, la asignación se implementa mediante el reemplazo escalar.

El análisis de escape es inmaduro

El documento sobre análisis de escape se publicó en 1999, pero no se implementó hasta JDK 1.6, y la tecnología no está muy madura hasta ahora.

La razón fundamental es que no hay garantía de que el consumo de rendimiento del análisis de escape sea superior a su consumo. Aunque después del análisis de escape, se puede realizar el reemplazo escalar, la asignación de pilas y la eliminación de bloqueos. Sin embargo, el análisis de escape en sí mismo también requiere una serie de análisis complejos, que en realidad es un proceso relativamente lento.

Un ejemplo extremo es que después del análisis de escape, se encuentra que ningún objeto no escapa. Entonces el proceso de análisis de escape se desperdicia.

Aunque esta tecnología no está muy madura, también es un medio muy importante de tecnología de optimización del compilador justo a tiempo.

Supongo que te gusta

Origin blog.csdn.net/qq_30264689/article/details/102911269
Recomendado
Clasificación