In the memory management of JAVA virtual machine, the concepts of heap, stack, method area and constant pool are often mentioned, and the understanding of theoretical knowledge often stays on the literal meaning, such as storing objects in heap memory, storing local variables in stack memory, storing string constant meter in constant pool, etc. This paper tries to pass these theoretical concepts through program samples through an interesting example Examples and diagrams can be expressed clearly, so that we can go deeper into the underlying knowledge.
- A const is defined in the StringJvm class_ String string constant and assign "Hello World";
- The main method defines str1 and str2 variables of String type. Both variables are assigned "Hello World";
- Compare the address values of str1 and str2;
- new a str3 of String type and initializes the value to "Hello World" through the constructor;
- Compare the address values of str3 and str3.
/** * Understanding JVM -- objects in heap memory * @author zhuhuix * @date 2020-05-19 * */ public class StringJvm { public static final String CONST_STRING="Hello World"; public static void main(String[] args) { String str1 =CONST_STRING; String str2 =CONST_STRING; System.out.println("str1==str2: "+ (str1 == str2)); System.out.println("str1==CONST_STRING: "+ (str1 == CONST_STRING)); String str3 =new String(CONST_STRING); System.out.println("str3==str1: "+(str3 == str1)); } }output
Let's start with two comparisons:
- The address value of str1 is equal to the address value of str2
- The address value of str1 is equal to const_ Address value of string
- The address value of str3 is not equal to the str1 address value, that is, it is not equal to the str2 value
Some students may have questions. It is clear that the values of the four strings are "Hello World", but the address values are equal or unequal. Here, we will introduce some basic concepts of JVM heap memory, stack memory and constant pool. Let's go to the figure above to explain:
- Define static constant CONST_STRING is put into the method area; when assigning, the JVM will put the "Hello World" string in the string constant pool for sharing, and assign the memory address to the static constant.
- When the str1 variable is defined and assigned to the string, the JVM first looks for the memory address with the same string value in the string constant pool and assigns the memory address to the str1 variable. So whether the address value of the printout str1 variable in the program is equal to the static constant const_ When the address value of string, the result is True
- When defining the str2 variable, the above 2 procedures are followed, that is, the address value of str2 is equal to const_ Address values for string and str1.
- When defining the str3 variable, we use the forced new object and construction method to pass the value. We know that the new object will allocate memory to the object in the heap, that is, the address value of str3 is referenced in the heap memory, so the address value of str3 must not be equal to the address value of str1.
The above illustration only explains why some address values are equal and some address values are not. Next, we create a tool class to obtain address values to verify the above theoretical knowledge.
First, we use the Unsafe class in Java to create a tool class and a static method, imitating the ability of C + + to manage memory manually to get the actual address value of the object.
/** * Get the memory address of the JVM object * @author zhuhuix * @date 2020-05-19 * */ public class ObjectAddress { private static Unsafe unsafe; static { try { Field field = Unsafe.class.getDeclaredField("theUnsafe"); field.setAccessible(true); unsafe = (Unsafe)field.get(null); } catch (Exception e) { e.printStackTrace(); } } public static long addressOf(Object o) { Object[] array = new Object[] ; long baseOffset = unsafe.arrayBaseOffset(Object[].class); int addressSize = unsafe.addressSize(); long objectAddress; switch (addressSize) { case 4: objectAddress = unsafe.getInt(array, baseOffset); break; case 8: objectAddress = unsafe.getLong(array, baseOffset); break; default: throw new Error("unsupported address size: " + addressSize); } return(objectAddress); }
Next, change the original program:
/** * Understanding JVM -- objects in heap memory * @author zhuhuix * @date 2020-05-19 * */ public class StringJvm { public static final String CONST_STRING="Hello World"; public static void main(String[] args) { System.out.println( "CONST_STRING Address value for:"+ObjectAddress.addressOf(CONST_STRING)); String str1 =CONST_STRING; String str2 =CONST_STRING; System.out.println( "str1 Address value for:"+ObjectAddress.addressOf(str1)); System.out.println("str1==str2: "+ (str1 == str2)); System.out.println("str1==CONST_STRING: "+ (str1 == CONST_STRING)); String str3 =new String(CONST_STRING); System.out.println("str3 Address value of :"+ ObjectAddress.addressOf(str3)); System.out.println("str3==str1: "+(str3 == str1)); } }
The output results are as follows:
Here we actually see the actual address value of the object, the static constant const_ The address value of string is equal to the address value of str1; the address value of str3 is not equal to the address value of str1.
Another important feature of runtime constant pool compared with Class file constant pool is that it is dynamic. Java language does not require that constants can only be generated at compile time. That is to say, the content of constant pool not preset in Class file can enter method area runtime constant pool. During runtime, new constants can also be put into the pool. This feature is used by developers A lot more is the String Class's intern() method.
Let's take a look at the above paragraph. The JVM tells us that the constant pool has dynamic characteristics. The current String object can be dynamically bound to the constant pool by using the String class's inter() method. For better understanding, let's transform the following example:
/** * Understanding JVM -- objects in heap memory * @author zhuhuix * @date 2020-05-19 * */ public class StringJvm { public static final String CONST_STRING="Hello World"; public static void main(String[] args) { System.out.println( "CONST_STRING Address value for:"+ObjectAddress.addressOf(CONST_STRING)); String str1 =CONST_STRING; String str2 =CONST_STRING; System.out.println( "str1 Address value for:"+ObjectAddress.addressOf(str1)); System.out.println("str1==str2: "+ (str1 == str2)); System.out.println("str1==CONST_STRING: "+ (str1 == CONST_STRING)); String str3 =new String(CONST_STRING); System.out.println("str3 new String Address value of :"+ ObjectAddress.addressOf(str3)); System.out.println("str3==str1: "+(str3 == str1)); //Dynamic binding with constant pool in program running process through intern method str3=str3.intern(); System.out.println("str3.intern()Address value of :"+ ObjectAddress.addressOf(str3)); System.out.println("str3.intern()after==str1: "+(str3 == str1)); } }
Look at the output: we find that the address value of str3 using this method is consistent with that of str1, which means that str3 also points to the initial memory allocation address in the constant pool.
After the above transformation of the code, and then use this diagram to explain it clearly.
The architecture design of the JVM is very delicate, which needs to be analyzed in the bottom layer; in the learning process of the system architecture, it is an essential step for us to write sample code and draw the prototype map by combining the principle.