JVM 字符串常量池的垃圾回收
JVM字符串常量池会进行垃圾回收吗?
会,但是对方法区的回收“成绩”比较难以令人满意,Java虚拟机规范中确实说过可以不要求虚拟机在方法区实现垃圾回收,而字符串常量池在方法区中。
假设一个字符串"everor"已经进入了常量池,但是当前系统中没有一个String对象引用常量池中的"everor"常量(即String str = new String("everor");),也没有其他地方引用了这个字面量(即String str = "everor";),如果这个时候发生了内存回收,而且有必要的话,这个"everor"常量就会被系统清理出常量池。
在HotSpot虚拟机中,方法区又被称为永久代,永久代这个概念在其它虚拟机(如BEA JRockit、IBM J9等)中是不存在的。但是使用永久代来实现方法区,并不是一个好主意,因为这样更容易遇到内存溢出问题。从JDK1.7开始,逐步使用元空间(MetaSpace)代替永久代的概念。元空间的内存大小取决于本地内存大小。元空间代替永久代,并不意味着字符串常量池就到了元空间,而是移到了堆中。
延伸:何时将字符串对象引用放入字符串常量池?
字符串常量池存放的是字符串对象的引用,字符串本身是一个对象,而对象是存放到堆中。当使用
String str1 = "hello";
String str2 = "hello";
时,首先会看字符串常量池中是否已经有对“hello”字符串对象的引用,如果有,就返回池中的实例引用,如果不存在,就会实例化一个字符串到堆中,再在池中存一个对该字符串的引用。因此str1和str2指向的是同一个字符串对象。Java能够做这样的优化,是因为字符串是不可变的,可以不担心数据冲突进行共享。
而当使用
String str3 = new String("hello");
时,不管字符串常量池中是否已经有hello字符串的引用,都会在堆中创建一个字符串,str3指向堆中的字符串,因此str3和str1 str2不是指向同一个对象。
当使用
String str4 = str3.intern();
时,intern()方法表示如果str3的字符串在池中有,就返回池中的引用,否则在池中创建一个引用,再返回该引用,因此str4 str1 str2指向的是同一个字符串对象。
参考
-《深入理解Java虚拟机》