方法区和运行时常量池溢出:
前面提到JDK1.7还是逐步“去永久代”的事情,在此就以测试代码观察一下这件事对程序的实际影响。
String.intern()是一个Native方法,它的作用是:如果字符串常量池中已经包含了一个等于此String对象的字符串,则返回代表池中这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。在JDK1.6及之前的版本中由于常量池分配在永久代内,我们可以通过-XX:PermSize和-XX:MaxPermSize限制方法区大小,从而间接限制其中常量池的容量。
public class RuntimeConstantPoolOOM {
/**
* -XX:PermSize=10M -XX:MaxPermSize=10M
* @param args
*/
public static void main(String[] args) {
List<String> list = new ArrayList<>();
int i = 0;
while (true){
list.add(String.valueOf(i++).intern());
}
}
}
-XX:PermSize=10M 方法区初始化大小
-XX:MaxPermSize=10M 方法区最大上限
运行结果:
JDK1.7 一切正常,并不会抛异常。 程序一直产生字符串常量,方法区大小为10M,程序正常 说明常量池不在方法区
JDK1.6:
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
at java.lang.String.intern(Native Method)
at test.main(test.java:13)
java.lang.OutOfMemoryError之后跟着 PermGen space,说明运行时常量池属于方法区(HotSpot虚拟机中的永久代)
public class RuntimeConstantPoolOOM {
/**
* -XX:PermSize=10M -XX:MaxPermSize=10M
* @param args
*/
public static void main(String[] args) {
String s = new StringBuilder("计算机").append("软件").toString();
System.out.println(s.intern() == s);
String s1 = new StringBuilder("ja").append("va").toString();
System.out.println(s1.intern() == s1);
}
}
运行结果:
JDK1.6: false false
JDK1.7: true false
JDK1.6中,intern()方法会把首次遇到的字符串实例复制到永久代中,返回的也是永久代中这个字符串实例的引用,而由StringBuilder创建的字符串实例在Java堆上,所以必然不是同一个引用,返回false。
JDK1.7中,intern()实现不会再复制实例,只是在常量池中记录首次出现的实例引用,因此intern()返回的引用和由StringBuilder创建的那个字符串实例是同一个。s.intern() == s 返回true。
s1返回false是因为“java”这个字符串在执行StringBuilder.toString()之前已经出现过,字符串常量池中已经有它的引用了,不符合“首次出现”的原则,而“计算机软件”这个字符串则是首次出现的,s1.inertn() == s1 返回false
"java" 字符串在StringBuilder.toString 之前出现的位置:https://blog.csdn.net/u014333083/article/details/80354900
public class JavaMethodAreaOOM {
/**
* -XX:PermSize=10M -XX:MaxPermSize=10M
* @param args
*/
public static void main(final String[] args) {
while (true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMObject.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return methodProxy.invokeSuper(objects,args);
}
});
enhancer.create();
}
}
static class OOMObject {
}
}