深入理解Java虚拟机案例代码P63代码
首先给出代码
package com.lsh.java.stackoverflow;
import java.util.HashSet;
import java.util.Set;
/**
* 运行时常量池导致的内存溢出异常
* 由于在JDK7以后,字符串常量池从方法区移动到了Java堆区,
* 因此我们需要显示Java堆区的最大容量便可以很轻易的让程序出现OOM
*
* -Xmx6M
* OutOfMemoryError:GC overhead limit exceeded
*/
public class RuntimeConstantPoolOOM {
// public static void main(String[] args) {
// Set<String> set = new HashSet<String>();
// short i = 0;
// while(true){
// set.add(String.valueOf(i++).intern());
// }
// }
public static void main(String[] args) {
String str1 = new StringBuilder("计算机").append("软件").toString();
System.out.println(str1.intern() == str1);
String str3 = "java";
//创建str3 == java,那么字符串常量池中已经有java,那么str2创建的java字符串在Java堆内存中
//自然,判断str2 == str3的话,地址不同,显然为false
String str2 = new StringBuilder("ja").append("va").toString();
//System.out.println(str3==str2);
//由于java字符串已经在字符串常量池中有引用,不符合intern方法的“首次遇到”原则,因此判断也是false
//如果注释掉String str3 = "java"; 那么判断就会返回true,因为字符串常量池中并没有java,所以str2.intern方法会返回一个引用
// 这个引用与str2相同,因此返回true
System.out.println(str2.intern() == str2);
}
}
方法的解释在代码注释中已经写的很清晰了,在这里再做以下总结
- 首先,本次环境是JDK8,书上给出案例是7
- 书上给出代码与本地实现有差别,具体差别表现在
String str2 = new StringBuilder("ja").append("va").toString();
System.out.println(str2.intern() == str2);
- 书上给出的答案为 false ,原因如下
- 因为”Java”这个字符串在执行
String-Builder-toString()
的时候就已经出现过了,那么字符串常量池就有他的引用 - 而对于
intern
方法,如果在常量池中没有字符串的引用,那么就生成一个在常量池中的引用,相反,则不生成,生成的引用和堆中的对象地址相同 - 在这里已经有
java
的引用了,不符合intern方法要求首次遇到的原则,那么判断自然会返回false
- 因为”Java”这个字符串在执行
- 注意,书上的环境为JDK7,
java
在加载sun.misc.Version
这个类的时候进入常量池 - 现在,在JDK8的环境下,以上代码运行结果为true,原因是在JDK8中并没有把Java加载进入字符串常量池
- 那么我们必须要在程序的开头加上一句
String str3 = "java";
这样我们就定义了一个字符串java
进入常量池,这样intern就不会返回和堆中对象地址相同的引用,自然也会返回false
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!