String.intern()
最近看java内存模型,提到String的intern()方法,发现还蛮纠结的,所以稍微整理一下。
一、String在内存中的位置
Class Test {
....
String str = “hello";
....
}
String的常用方式如上,"hello"这种形式称为字面量。编译生成class文件时,字面量存在class文件的常量池中。运行程序要用到Test类时,Test.class文件内容会被加载到内存的方法区中。class中常量池的大部分内容会被加载到运行时常量池,但String不是。“hello”的引用会创建在字符串常量池中,而“hello”对象创建中堆中,如下图所示。
每个线程独享的空间包括程序计数器、本地方法栈、虚拟机栈。程序计数器用于线程切换时标记代码执行的位置。本地方法栈,用于执行非java的方法,如c/c++的方法等。虚拟机栈,就是执行用于java方法了,每一个java方法对应一个栈帧。
“hello”对象创建后,String str对象还没有创建。主线程执行到String str = “hello"时,首先用equals方法判断字符串常量池中是否已经存在“hello”对象的引用,如果已存在,就将str指向该对象,如下图所示。如果不存在,就在堆中创建,并在字符串常量池中存放其引用,然后将引用赋值给str。如下图所示。
Class Test {
....
String str = “hello";
String str1 = “hello";
String str2 = “hello";
....
}
如果像这样创建多个”hello“,都不会申请新的内存,如下图:
接下来是new String创建方式:
Class Test1 {
....
String str = new String("hello");
String str1 = “hello";
....
}
用new创建String时,总是会申请一块新内存,不考虑常量池中是否已存在。如上代码,加载Test1时,首先用字面量”hello“创建对象,并将引用放入到字符串常量池。之后创建str时,会再申请一块内存,新建另一个”hello“,并将引用赋值给str。如下图所示。创建str1时,去字符串常量池中查找,找到已存在的上面一个”hello“,所以不需要申请新内存,直接将其引用赋值给str1.
二、intern基本介绍
String类维护自己的字符串常量池,它是全局共享的。调用String.intern方法时,判断字符串在常量池中是否存在(根据equals方法判断),如果存在,就返回常量池中对应字符串的引用;如果不存在,就将此字符串的值加入到常量池中,并返回此字符串的引用。
注意,字符串常量池存放的是对象的引用,不是对象本身,java的对象创建都在堆中。
intern的作用是什么呢?
intern()的作用是,String b=a.intern();则直接将b指向了字符串常量池中已有的一个字符串,不需要新开辟一块内存,节省空间。但字符串常量池的大小是有限的,如果太多,也会影响效率。
三、jdk7下intern示例
四、jdk6/jdk7中intern
前面图示中,internedString都位于永久代中的字符串常量池中,这是jdk6下的情况。而在jdk7中,internedString被挪到了heap区,且内部实现也有所变化。
jdk6中,intern()将首次出现的字符串复制到字符串常量池,并返回引用。 jdk7中,intern()不再复制实例,只在常量池中记录首次出现的实例引用,并返回该引用。