项目代码中用到反射,伴随大量的NoSuchFieldException异常,发现cpu飙高,排查后发现跟String.intern有关。
在Class中有连个常见的方法:
Ø public Field getField(String name)
Ø getMethod(String name, Class<?>... parameterTypes)
进入这个方法的实现,发现都会调用searchXXX的方法,已searchFields为例:
private Field searchFields(Field[] fields, String name) {
String internedName = name.intern();
for (int i = 0; i < fields.length; i++) {
if (fields[i].getName() == internedName) {
return getReflectionFactory().copyField(fields[i]);
}
}
return null;
}
注意这里的name.intern()调用,jdoc它的是说明如下:
A pool of strings, initially empty, is maintained privately by the class String.
When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the [equals(Object)](http://my.oschina.net/u/1162189/admin/eclipse-javadoc:?=coupon-dev/G:\/Program Files/Java/jdk1.6.0_33/jre/lib/rt.jar<java.lang(String.class?String~intern??equals?Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.
简单的说,String.intern()返回String pool中的引用,如果String pool中不存在,则先在String pool中加入一个字符串然后返回该引用。因此,如果s.equals(t) 是true,则 s.intern() == t.intern() 也为true。
问题来了,由于String pool位于perm区,如果随着String pool越来越大,就会引发full gc(只有full gc才会回收perm,另外,perm区的内存在很久很久以前的jdk中是不能被垃圾回收的,jdk1.2以后都可以回收);另外,String pool是hashtable(实际是weakreference)当这个table大了以后,无论是插入还是查找,都会付出越来越大的代价。
综合上述情况大量调用String.intern()并且大部分未能在string pool中找到已存在的实例,会引发String pool越来越大,进而导致intern方法效率降低,当string pool大到一定程度后,还会引发fgc。因此,慎用String.intern。