¿Cómo obtiene la JVM la longitud de la matriz?
Esta nota registra principalmente las matrices que escribimos en java, como int arr [] = {1,2,3};, luego usamos int len = arr.length; cómo el jvm subyacente obtiene la longitud de la
matriz ; la matriz está en La capa inferior de jvm se genera dinámicamente, lo que significa que no es estática. Es similar al código hash de un objeto. Si no ha anulado el método hashcode, el código hash del objeto es la dirección de memoria del objeto por defecto, por lo que el código hash también se genera dinámicamente , Nuestra matriz es la misma. Después de definir la matriz, la matriz se puede generar, agregar y eliminar dinámicamente en tiempo de ejecución, por lo que la longitud de la matriz también se genera dinámicamente; aquí usamos el programa y HSDB para probar la matriz Cómo obtener la longitud en la parte inferior del jvm, primero mire el siguiente programa:
public class T0819 {
public static void main(String[] args) {
int arr [] ={
1,2,3};
int length = arr.length;
System.out.println(length);
}
}
Revisemos la información del código de bytes a través de javap -verbose T0819.class
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=3, args_size=1
0: iconst_3
1: newarray int
3: dup
4: iconst_0
5: iconst_1
6: iastore
7: dup
8: iconst_1
9: iconst_2
10: iastore
11: dup
12: iconst_2
13: iconst_3
14: iastore
15: astore_1
16: aload_1
17: arraylength
18: istore_2
19: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
22: iload_2
23: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
26: return
LineNumberTable:
line 7: 0
line 8: 16
line 9: 19
line 10: 26
LocalVariableTable:
Start Length Slot Name Signature
0 27 0 args [Ljava/lang/String;
16 11 1 arr [I
19 8 2 length I
}
Mire el contador del programa 17, arraylength. Para decirlo sin rodeos, averiguar cómo se obtiene dinámicamente la longitud de la matriz en el jvm depende de cómo el jvm procesa la instrucción de código de bytes arraylength; aquí encontré un bloque de código de cómo openjdk obtiene la longitud de la matriz :
CASE(_arraylength):
{
arrayOop ary = (arrayOop) STACK_OBJECT(-1);
CHECK_NULL(ary);
SET_STACK_INT(ary->length(), -1);
UPDATE_PC_AND_CONTINUE(1);
}
int length() const {
return *(int*)(((intptr_t)this) + length_offset_in_bytes());
}
El código CASE es obtener la longitud de nuestra matriz. La tercera línea de código en el caso es obtener la longitud y colocarla en la pila.
Principalmente analizamos el método length (), que usa el puntero this para obtener la primera posición de la matriz, y luego agregamos El desplazamiento de la matriz se agrega para obtener la longitud de nuestra matriz, miramos la siguiente figura:
((intptr_t) this) = obtener la primera posición de nuestra matriz
length_offset_in_bytes () = obtener el desplazamiento de nuestra matriz en la memoria
y luego agregar , Toma la dirección y obtén la longitud de nuestra matriz
Veamos la función length_offset_in_bytes () para obtener el desplazamiento
//如果不压缩,则在arrayOopDesc中声明的非静态字段之后分配
//如果压缩,它将占用oopDesc中_klass字段的后半部分
static int length_offset_in_bytes() {
return UseCompressedClassPointers ? klass_gap_offset_in_bytes() :
sizeof(arrayOopDesc);
}
Mire el código anterior, hay un parámetro UseCompressedClassPointers, este parámetro también es parte de nuestro ajuste de jvm, ¿qué significa este parámetro? Sabemos por las notas anteriores que nuestro diseño de memoria de objetos tiene un encabezado de objeto, datos de instancia y relleno de alineación;
y el encabezado de objeto se divide en palabra de marca, puntero de tipo y longitud de matriz, y la compresión de puntero de tipo y los bytes sin comprimir no lo son Lo mismo,
entonces , ¿cuál es la diferencia entre UseCompressedClassPointers y UseCompressedoops?
UseCompressedoops comprime la longitud del puntero del objeto, mientras que UseCompressedClassPointers comprime la longitud del puntero del objeto Klass. Si habilitamos UseCompressedoops, entonces UseCompressedClassPointers está activado de forma predeterminada, lo que
significa que UseCompressedoops incluye UseCompressedClassPointers.
Primero, analicemos la matriz: el
Klass correspondiente es: instancia de TypeArrayKlass El
oop correspondiente es: instancia de TypeArrayOop
Y el bloque de código del puntero de tipo en jvm es:
union _metadata {
Klass* _klass; 8B
narrowKlass _compressed_klass; 4B
} _metadata;
Es un consorcio, todo el consorcio ocupa 8B de espacio, si estamos comprimidos, entonces se usará 4B, si no comprimimos, entonces se usará 8B, entonces si comprimimos, ¿es una pérdida de espacio? El comentario del método length_offset_in_bytes () ha sido escrito, comenzaremos a analizar todo el problema a continuación.
Disposición de la memoria de
objetos Encabezado del objeto
Marca Palabra
Puntero de Klass
Longitud de la matriz
Datos de la instancia
Relleno
Si habilitamos la compresión del puntero:
Marque Word 8B
Puntero Klass 4B
longitud de la matriz 4B
luego longitud del puntero + longitud de la matriz = 4B + 4B = 8B
si no comprimimos es = 8B + 4B = 12B
Entonces entendemos la oración en los comentarios de código "Si está comprimido, ocupará la segunda mitad del campo _klass en oopDesc".
Si está comprimido, el puntero de tipo es 4B, porque nuestro
union _metadata {
Klass* _klass; 8B
narrowKlass _compressed_klass; 4B
} _metadata;
Es 8B. Comprimido y usado es _compressed_klass en la unión, y nuestra longitud de matriz es 4B, por lo que usamos 4B en la segunda mitad de _klass, lo que significa que, es decir, nuestra longitud de matriz es La segunda mitad de la unión, si la compresión del puntero está activada.
Pasamos a través de nuestra longitud de matriz HSDB para ver lo siguiente:
estamos en compresión de puntero abierto, es --metadata._compressed_klass, veamos la siguiente vista de memoria:
cerramos la compresión de puntero: -XX: -UseCompressedClassPointers
a fancy La imagen es para saber que la compresión del puntero está desactivada.