The following figure demonstrates how arrays are implemented in the memory.
An array extends java/lang/object. The field Tag points to the type descriptor of this array ([ClassA for this example). The single array elements themselves are of type ClassA and have a Tag which points to the type descriptor of ClassA. For this reason arrays of base types like ([B, [S …) need a class descritor as well.
The field length denotes the number of elements of the array (16 bit). heap is used for the garbage collection (Heap Manager and Garbage Collection). In between there is a byte which contains an array bit. This is used for type checking. The P bit is for garbage collection. It is set for arrays of primitive types
When accessing array elements the code generator inserts code to check if the element index is in the range 0 to length.
If the field length is used in Java the Bytecode instruktion arraylength is used. This instruktion has to be translated so that the length field is accessed.
The Java code
static int[]a1 = new int[4];
will be translated into Bytecode
0 iconst_4 1 newarray int 3 putstatic Test.a1 : int[]
When regular objects are created the constructor of the supertype is automatically inserted into the Bytecode. When arrays are created there is no such call. The constructor of Object has to be inserted by the code generator and the heap manager has to add the size of java/lang/Object to the size of the array. As long as java/lang/Object has no fields the size is 0 and we can also omit the call of the constructor.
In the Bytecode newarray has a type (here int). When generating the SSA the reference to this type [I has to be fetched from the object directory.
For
static Object[] a1 = new ClassA[4];
the Bytecode looks like:
0 iconst_4 1 anewarray org/deepjava/junitTarget/comp/objects/ClassA 4 putstatic org/deepjava/junitTarget/comp/objects/InheritanceTest.a1: Ljava/lang/Object[];
Here, as well, the reference to [ClassA has to be fetched from the object directory when generating the SSA. There are cases when there is no entry for [ClassA. This is the case when there is never a type check for the type [ClassA. Then the SSA has a reference to null and the Tag can be 0 as well.
Let us consider the following case:
int[][] a = new int[2][3]
The memory will look like
[[I and [I are the corresponding type descriptors. When accessing an element one has to access a reference in the first dimension followed by the element access in the second dimension.
The following code
short[][] a = new short[2][3]
leads to the Bytecode
0 iconst_2 1 iconst_3 2 multianewarray short[][] 6 putstatic org/deepjava/runtime/mpc555/test/ArrayTest3.a1: short[][]
If an array is immediately initialized
short[][] a2 = {{1,2},{3,4}};
the Bytecode will be
9 iconst_2 10 anewarray [S 13 dup 14 iconst_0 15 iconst_2 16 newarray short 18 dup 19 iconst_0 20 iconst_1 21 sastore 22 dup 23 iconst_1 24 iconst_2 25 sastore 26 aastore 27 dup 28 iconst_1 29 iconst_2 30 newarray short etc.
The problem with the first case is, that only the type descriptor [[I would be linked. However, [I must be created as well. In the method multianewarray of the heap manager the array must be allocated and the references to [[I and [I must be set. The reference to [[I is passed as a parameter. In [[I there is the reference to [I. This reference must be fetched and inserted into the object at the appropriate position.
Similar thoughts are true for arrays with higher dimensions. As well, the instruction multianewarray is used solely for special purposes.
Here, the same conditions are true as described above.
ClassA[][][] a1 = new ClassA[3][2][2];
This leads to Bytecode
0 iconst_3 1 iconst_2 2 iconst_2 3 multianewarray Lch/ntb/junitTarget/comp/arrays/A[][][]; 7 astore_1 [a1]
The reference to [[[ClassA; can be found already in the SSA. The heap manager has to fetch the base types [[ClassA;, [ClassA; and ClassA; from the type descriptor recursively.