jvm内存-方法区,String常量池
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jvm内存-方法区,String常量池相关的知识,希望对你有一定的参考价值。
方法区
线程共享。
当JVM使用类装载器装载某个类时,首先获取class文件,提取该文件的内容信息,将这些信息存储到方法区,最后返回一个class实例。方法区用于存储已经被虚拟机加载的类信息(class)(版本,字段,方法,接口等描述信息),常量,静态变量(static),即时编译期编译后的代码数据等。称为“永久代”。GC在这区域较少出现,内存回收的主要目标是针对常量池的回收和对类的卸载(某个类不再使用)。
运行时常量池
是方法区的一部分,class文件中除了有类的版本,字段,方法,接口等描述信息以外,还有一项信息是常量池。
常量池:用于存放编译期生成的各种字面量(final修饰)和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。运行时常量池对于class文件常量池具备动态性,在程序运行期间也可以将新常量放入池中。(如final修饰的变量,可以不在声明时赋值,在运行时动态赋值,但是一旦赋值后不能改变)
String类型是由final修饰的,它是常量池中最常见的一种类型
在JAVA虚拟机(JVM)中存在着一个字符串池,其中保存着很多String对象,并且可以被共享使用,因此它提高了效率。由于String类是final的,它的值一经创建就不可改变,因此我们不用担心String对象共享而带来程序的混乱。字符串池由String类维护,我们可以调用intern()方法来访问字符串池。
String str = "helloword";//str指向常量池(字符串池)中的“helloworld”
String str1 = new String("helloword");//在堆中创建新对象,str1指向堆中对象
System.out.println(str==str1);// 运行后结果为false
//str1在new的过程中放在了堆引用,而str是放在常量池中的引用,
// 而’==’对比的是引用信息,所以是false.
System.out.println(str==str1.intern());//运行结果是true
//str1.intern()这个方法是一个native方法,
// 它是看String常量池中有没有这个引用如果有,则返回这个常量池引用,
// 如果没有则把它放到常量池中.
其他的几个例子
String a = "a1";
String b = "a" + 1;
System.out.println((a == b)); //result = true
分析:JVM对于字符串常量的"+"号连接,将程序编译期,JVM就将常量字符串的"+"连接优化为连接后的值,拿"a" + 1来说,经编译器优化后在class中就已经是a1。在编译期其字符串常量的值就确定下来,故上面程序最终的结果都为true。
String a = "ab";
String bb = "b";
String b = "a" + bb;
System.out.println((a == b)); //result = false
分析:JVM对于字符串引用,由于在字符串的"+"连接中,有字符串引用存在,而引用的值在程序编译期是无法确定的,即"a" + bb无法被编译器优化,只有在程序运行期来动态分配并将连接后的新地址赋给b。所以上面程序的结果也就为false。
String a = "ab";
final String bb = "b";
String b = "a" + bb;
System.out.println((a == b)); //result = true
分析:和[2]中唯一不同的是bb字符串加了final修饰,对于final修饰的变量,它在编译时被解析为常量值的一个本地拷贝存储到自己的常量池中或嵌入到它的字节码流中。所以此时的"a" + bb和"a" + "b"效果是一样的。故上面程序的结果为true。
以上是关于jvm内存-方法区,String常量池的主要内容,如果未能解决你的问题,请参考以下文章