The Java constant pool is an enduring topic and a favorite of interviewers. There are a variety of questions. Xiaocai has already heard about the constant pool, so I will summarize it well this time.
Recommended: java video tutorial
jvm virtual memory distribution:
The program counter is used by the jvm to execute the program The pipeline stores some jump instructions. This is too advanced for Xiao Cai to understand.
The local method stack is the stack used by the jvm to call operating system methods.
The virtual machine stack is the stack used by jvm to execute java code.
The method area stores some constants, static variables, class information, etc., which can be understood as the storage location of class files in memory.
The virtual machine heap is the heap used by jvm to execute java code.
The constant pool in Java is actually divided into two forms: static constant pool and runtime constant pool.
The so-called static constant pool is the constant pool in the *.class file. The constant pool in the class file not only contains string (number) literals, but also contains class and method information, occupying absolutely no part of the class file. Most of the space.
The runtime constant pool means that after the jvm virtual machine completes the class loading operation, it loads the constant pool in the class file into the memory and saves it in the method area. We often call it the constant pool. , refers to the runtime constant pool in the method area.
Next we will quote some popular constant pool examples on the Internet and then explain them.
String s1 = "Hello"; String s2 = "Hello"; String s3 = "Hel" + "lo"; String s4 = "Hel" + new String("lo"); String s5 = new String("Hello"); String s6 = s5.intern(); String s7 = "H"; String s8 = "ello"; String s9 = s7 + s8; System.out.println(s1 == s2); // true System.out.println(s1 == s3); // true System.out.println(s1 == s4); // false System.out.println(s1 == s9); // false System.out.println(s4 == s5); // false System.out.println(s1 == s6); // true
First of all, let me explain that in java, when using the == operator directly, what is compared is the reference address of the two strings, not the content. Please use String.equals() to compare the content.
s1 == s2 is very easy to understand. When assigning values to s1 and s2, both use string literals. To put it bluntly, it means directly writing the string to death. During compilation, this kind of literal will be directly put into the constant pool of the class file to achieve reuse. After loading the runtime constant pool, s1 and s2 point to the same memory address, so they are equal.
s1 == s3 has a pitfall. Although s3 is a dynamically spliced string, all the parts involved in the splicing are known literals. During compilation, this splicing will be optimized. , the compiler directly spells it for you, so String s3 = "Hel" "lo"; is optimized into String s3 = "Hello"; in the class file, so s1 == s3 is established.
s1 == s4 is certainly not equal. Although s4 is also spliced, the new String("lo") part is not a known literal and is an unpredictable part. The compiler will not optimize it. The result cannot be determined until runtime. Combined with the string invariance theorem, who knows where s4 is allocated, so the address must be different. A simple diagram is provided to clarify the idea:
s1 == s9 is not equal, and the reason is similar. Although s7 and s8 use string literals when assigning values, when they are spliced into s9, As two variables, s7 and s8 are unpredictable. After all, the compiler is a compiler and cannot be used as an interpreter, so no optimization is done. When it is run, the new string spliced by s7 and s8 will be at the address in the heap. Not sure, it cannot be the same as the s1 address in the constant pool in the method area.
s4 == s5 requires no explanation. It is absolutely not equal. Both are in the heap, but have different addresses.
s1 == s6 These two equality are entirely due to the intern method. s5 is in the heap and the content is Hello. The intern method will try to add the Hello string to the constant pool and return it in the constant pool. Because there is already a Hello string in the constant pool, the intern method directly returns the address; and s1 already points to the constant pool during compilation, so s1 and s6 point to the same address and are equal.
At this point, we can draw three very important conclusions:
We must pay attention to the behavior at compile time in order to better understand the constant pool.
The constants in the runtime constant pool basically come from the constant pool in each class file.
When the program is running, unless constants are manually added to the constant pool (such as calling the intern method), the jvm will not automatically add constants to the constant pool.
The above only involves the string constant pool. In fact, there are also integer constant pools, floating-point constant pools, etc., but they are all similar. However, you cannot manually add constants to the numerical type constant pool. The constants in the constant pool have been determined when the program starts. For example, the constant range in the integer constant pool is: -128~127. Only numbers in this range can be used in the constant pool.
Practice
Having said so much theory, let’s touch on the real constant pool.
As mentioned earlier, there is a static constant pool in the class file. This constant pool is generated by the compiler and is used to store literals in java source files (this article only focuses on literals). Suppose we There is the following java code:
String s = "hi";
为了方便起见,就这么简单,没错!将代码编译成class文件后,用winhex打开二进制格式的class文件。如图:
简单讲解一下class文件的结构,开头的4个字节是class文件魔数,用来标识这是一个class文件,说白话点就是文件头,既:CA FE BA BE。
紧接着4个字节是java的版本号,这里的版本号是34,因为笔者是用jdk8编译的,版本号的高低和jdk版本的高低相对应,高版本可以兼容低版本,但低版本无法执行高版本。所以,如果哪天读者想知道别人的class文件是用什么jdk版本编译的,就可以看这4个字节。
接下来就是常量池入口,入口处用2个字节标识常量池常量数量,本例中数值为00 1A,翻译成十进制是26,也就是有25个常量,其中第0个常量是特殊值,所以只有25个常量。
常量池中存放了各种类型的常量,他们都有自己的类型,并且都有自己的存储规范,本文只关注字符串常量,字符串常量以01开头(1个字节),接着用2个字节记录字符串长度,然后就是字符串实际内容。本例中为:01 00 02 68 69。
接下来再说说运行时常量池,由于运行时常量池在方法区中,我们可以通过jvm参数:-XX:PermSize、-XX:MaxPermSize来设置方法区大小,从而间接限制常量池大小。
假设jvm启动参数为:-XX:PermSize=2M -XX:MaxPermSize=2M,然后运行如下代码:
//保持引用,防止自动垃圾回收 List<String> list = new ArrayList<String>(); int i = 0; while(true){ //通过intern方法向常量池中手动添加常量 list.add(String.valueOf(i++).intern()); }
程序立刻会抛出:Exception in thread "main" java.lang.outOfMemoryError: PermGen space异常。PermGen space正是方法区,足以说明常量池在方法区中。
在jdk8中,移除了方法区,转而用Metaspace区域替代,所以我们需要使用新的jvm参数:-XX:MaxMetaspaceSize=2M,依然运行如上代码,抛出:java.lang.OutOfMemoryError: Metaspace异常。同理说明运行时常量池是划分在Metaspace区域中。具体关于Metaspace区域的知识,请读者自行搜索。
更多java知识请关注java基础教程栏目。
The above is the detailed content of Detailed graphic and text explanation of java constant pool. For more information, please follow other related articles on the PHP Chinese website!