String.intern()

2017-11-20  本文已影响0人  水欣
  1. String.intern()原理
    String.intern()是一个Native方法,底层调用c++的StringTable::intern方法,源码注释:当调用intern方法时,如果常量池中已经有该字符串,则返回池中的字符串;否则将此字符串添加到常量池中,并返回字符串的引用。
class Test {
    public static void main(String args[]) {
        String s1 = new StringBuilder().append("String").append("Test").toString();
        System.out.println(s1.intern() == s1);

        String s2 = new StringBuilder().append("ja").append("va").toString();
        System.out.println(s2.intern() == s2);
    }
}

在JDK6和JDK7中结果不一样:

对于常量s1,常量池中没有"StringTest"字符串,s1.intern()和s1都是指向Java对象上的String对象。
对于变量s2,常量池中一开始就已经存在“java”字符串,所以s2.intern()返回常量池中“java”字符串的引用。

  1. String.intern()性能
    常量池底层使用StringTable数据结构保存字符串引用,实现和HashMap类似,根据字符串的hashcode定位到对应的数组,遍历链表查找字符串,当字符串比较多时,会降低查询效率。
    在JDK6,由于常量池在PermGen中,受到内存大小的限制,不建议使用该方法,在JDK7、8中,可以通过-XX:StringTableSize参数StringTable大小,下面通过几个测试用例看下intern方法的性能
    public static void main(String[] args) {
        System.out.println(cost(1000000));
    }

    public static long cost(int num) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < num; i++) {
            String.valueOf(i).intern();
        }
        return System.currentTimeMillis() - start;
    }
}

执行一百万次intern()方法,不同StringTableSize的耗时情况如下:
1、-XX:StringTableSize=1009, 平均耗时23000ms;
2、-XX:StringTableSize=10009, 平均耗时2200ms;
3、-XX:StringTableSize=100009, 平均耗时200ms;
4、默认情况下,平均耗时400ms;

在默认StringTableSize下,执行不同次intern()方法的耗时情况如下:
1、一万次,平均耗时5ms;
2、十万次,平均耗时25ms;
3、五十万次,平均耗时130ms;
4、一百万次,平均耗时400ms;
5、五百万次,平均耗时5000ms;
6、一千万次,平均耗时15000ms;

从这些测试数据可以看出,尽管在Java 7以上对intern()做了细致的优化,但其耗时仍然很显著,如果无限制的使用intern()方法,将导致系统性能下降,不过可以将有限值的字符串放入常量池,提高内存利用率,所以intern()方法是一把双刃剑。

上一篇 下一篇

猜你喜欢

热点阅读