第二章:方法区和运行时常量池溢出
Posted use-d
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第二章:方法区和运行时常量池溢出相关的知识,希望对你有一定的参考价值。
由于运行时常量池属于方法区的一部分,因此两个区域放在一块执行。
String.intern()是一个Native方法,它的作用是如果字符串常量池中已经包含了此String对象的字符串,则返回代表池中这个字符串的String对象;否则将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。
可以通过以下代码测试运行时常量池溢出:
public class Test { public static void main(String[] args) { int i =0; List<String> list = new ArrayList(); while(true) { list.add(String.valueOf(i++).intern()); } } }
可以在抛出的异常后面发现“Perm space”信息。
可以使用String.intern()测试运行时常量池:
public class Test1 { public static void main(String[] args) { String str1 = new StringBuilder("111").append("-222").toString(); System.out.println(str1.intern()==str1); String str2= new StringBuilder("jav").append("a").toString();; System.out.println(str2.intern()==str2); } } 结果: true false
JDK1.7中的intern实现不会复制实例,只是在常量池中记录首次出现的实例引用,因此intern()返回的引用和由StringBuilder创建的那个字符串实例是同一个。
对str2比较返回false是因为“java”字符串在执行StringBuilder.toString()之前已经出现过了,字符串常量池中已经有它的引用了,不符合“首次出现”的原则。
方法区用于存放Class相关的信息,如类名、访问修饰符、常量池、字段描述、方法描述等,对于这些区域的测试,基本的思路是运行时产生大量的类填充方法区,直到溢出。
可以借助GCLib直接操作字节码运行时产生大量的动态类:
public class Test1 { 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.invoke(objects,args); } }); enhancer.create(); } } static class OOMOBject{ } }
除了GCLib字节码增强和动态语言之外,常见的还有大量JSP或者动态生成JSP文件的应用、基于OSGi的应用等
以上是关于第二章:方法区和运行时常量池溢出的主要内容,如果未能解决你的问题,请参考以下文章