Java程序学习手册

今天给大家带来的是关于怎么让java6实现String.intern的方法,同时这个方法运用在java7和java8里面做些什么调整?

字符串池是一个有名字符串标准化的,经过唯一使用共享string用的相同值却不一样地址表示字符串的一个过程。能够用自己定义的Map<String, String>依据要用到的soft引用和weak引用,让map的值成为一个标准值进行实现这个目的,或者也能够用别的方法比如运用jdk提供的一个String.intern()。

如果在java6里面频繁池会市区控制的haunted,在很大的几率下面触发了OutOfMemoryException,因此是禁止使用String.intern()

以前共享的string的美好对象都是存储在permgen里面的一部分固定了大小的部分,主要的是用来存放加载类型对象以及字符串池的。在permgen里除了明确了的共享字符串另外还有程序里面使用过的字符串。

Java6里面的permgen就是它字符串池的最大问题的一个位置。Permgen有固定的大小而且在运作时候也是不能扩展的。但是能够通过-XX:MaxPermSize=N配置进行调整它的一个大小,这个大小是按照不一样平台默认的permgen的大小在32m和96m之间。因此你在运用String.intern要小心仔细,按照它固定的大小扩展你需要的大小。最好是不要用这个方法intern任何不能控制的用户输入。这就是为什么java6里面发部分是通过手动操作管理map实现字符串池

Java 7 里面的 Oracle的产生让工程师们对于字符串池的一个认识逻辑有了很大改变,将字符串池的位置调在了heap里面。这也就是说所有的字符串都会保存在heap里就和其他普通的对象一样,再也不会被固定在一个内存空间里被限制了。这一系列的改变就让工程师们足够的确信再次在java7里面使用String.intern()。

在jvm中的字符串池的数据也会被垃圾收集,比如这些数值在应用里面没有任何引用的话,就意味这interned的字符串在作用领域外面没有其他任何的引用作用,它就会被jvm里的字符串池给当做垃圾收集掉

如果被再次垃圾收集和定位到了堆中的话,那么jvm的字符串池看起来就像是一个非常适合存放字符串的位置了。但理论上面是违背运用字符串的话会被从池里面收集掉,要是外部输入了一个字符并且池里面已经存在了就能够有效节省内存了。因此不得不认同这个看起来是一个完美的节省内存的策略。

字符串池是经过一个具有固定容量的hashmap里任何一个元素都拥有一样hash值的字符串列表。

系统里默认的池大小1009。在早期的java6里面是一个常量,并随后的java6u30 到java6u41里是能够调整配置的。但是在java7里能够配置的,你是一定要指定参数的-XX:StringTableSize=N,N是字符串池map的大小也是确保了它可以为了性能的调节优化型提前准备好的大小。

但是在javva6这个参数是没有很多大帮助,一般到了后面都是直接忽略了。但是在java7里面你就能够按照你所需要的应用程序预先设置好了string池的一个大小,

有的为了程序需要你一定会设置一个更大的-XX:StringTalbeSize 值,但是你要是想更多运用String.intern()不然这个办法就会很快的下降到0(池的大小)

很多人不会注意到intern小于100字符的一个字符串的依赖情况,以下是默认池大小的应用程序的日志第一列是已经 intern 的字符串数量,第二列 intern 10,000 个字符串所有的时间(秒)

0; time = 0.0 sec

50000; time = 0.03 sec

100000; time = 0.073 sec

150000; time = 0.13 sec

200000; time = 0.196 sec

250000; time = 0.279 sec

300000; time = 0.376 sec

350000; time = 0.471 sec

400000; time = 0.574 sec

450000; time = 0.666 sec

500000; time = 0.755 sec

550000; time = 0.854 sec

600000; time = 0.916 sec

650000; time = 1.006 sec

700000; time = 1.095 sec

750000; time = 1.273 sec

800000; time = 1.248 sec

850000; time = 1.446 sec

900000; time = 1.585 sec

950000; time = 1.635 sec

1000000; time = 1.913 sec

通过Core i5-3317U@1.7Ghz CPU测试能够看的出来,它在jvm字符串池拥有了一百万个字符串,并且是呈成线性增长的,我们在到的近似每秒intern 5000个字符,但是它在内存里处理大量数据的应用程序来讲还是太慢了。 

那重新看看有码互联调整后的-XX:StringTableSize=100003的运行测试结果吧

50000; time = 0.017 sec

100000; time = 0.009 sec

150000; time = 0.01 sec

200000; time = 0.009 sec

250000; time = 0.007 sec

300000; time = 0.008 sec

350000; time = 0.009 sec

400000; time = 0.009 sec

450000; time = 0.01 sec

500000; time = 0.013 sec

550000; time = 0.011 sec

600000; time = 0.012 sec

650000; time = 0.015 sec

700000; time = 0.015 sec

750000; time = 0.01 sec

800000; time = 0.01 sec

850000; time = 0.011 sec

900000; time = 0.011 sec

950000; time = 0.012 sec

1000000; time = 0.012 sec

上面不难看出来插入字符串的时间近似在常量里的下面是相同的一个设置结果但是有码互联再继续测试下,把池里插入1000万个字符串的结果吧。

2000000; time = 0.024 sec

3000000; time = 0.028 sec

4000000; time = 0.053 sec

5000000; time = 0.051 sec

6000000; time = 0.034 sec

7000000; time = 0.041 sec

8000000; time = 0.089 sec

9000000; time = 0.111 sec

10000000; time = 0.123 sec

接下来在精确到1,000,003来看看吧

1000000; time = 0.005 sec

2000000; time = 0.005 sec

3000000; time = 0.005 sec

4000000; time = 0.004 sec

5000000; time = 0.004 sec

6000000; time = 0.009 sec

7000000; time = 0.01 sec

8000000; time = 0.009 sec

9000000; time = 0.009 sec

10000000; time = 0.009 sec

上面的测试也是可以看的出来时间与0到100万之间是没有很大区别都是非常平均的。可以这么理解,如果池大小足够的话,我们的笔记本也是可以每秒添加1,000,000个字符。但是我们还有必要通过手工进行管理字符串池么?

  那么我们来对比 JVM 字符串池和 WeakHashMap<String, WeakReference<String>> 它是能够进行模拟 JVM 字符串池。一下的方法是用来替换 String.intern:

private static final WeakHashMap<String, WeakReference<String>> s_manualCache =

new WeakHashMap<String, WeakReference<String>>( 100000 );

private static String manualIntern( final String str )

{

final WeakReference<String> cached = s_manualCache.get( str );

if ( cached != null )

{

final String value = cached.get();

if ( value != null )

return value;

}

s_manualCache.put( str, new WeakReference<String>( str ) );

return str;

}

对于相同的手工池测试:

0; manual time = 0.001 sec

50000; manual time = 0.03 sec

100000; manual time = 0.034 sec

150000; manual time = 0.008 sec

200000; manual time = 0.019 sec

250000; manual time = 0.011 sec

300000; manual time = 0.011 sec

350000; manual time = 0.008 sec

400000; manual time = 0.027 sec

450000; manual time = 0.008 sec

500000; manual time = 0.009 sec

550000; manual time = 0.008 sec

600000; manual time = 0.008 sec

650000; manual time = 0.008 sec

700000; manual time = 0.008 sec

750000; manual time = 0.011 sec

800000; manual time = 0.007 sec

850000; manual time = 0.008 sec

900000; manual time = 0.008 sec

950000; manual time = 0.008 sec

1000000; manual time = 0.008 sec

在jvm的内存足够大的时候,手工编写的池就能够很好的提供不错的性能。但在jvm字符串池包含了12.72的字符串的是偶并且在jvm拥有想用的性能特征的话。那这就非常值得在你的应用里面去掉所有的手工字符串池。在 Java 7u40+ 以及 Java 8 中的 String.intern()

如果Java7u40 版本扩展了字符串池的大小更新到 60013这个值的话就可以在池中包含大约到3000个独立的字符串了。一般来讲,这个数字对于需要保存的数据来讲是够了。同样的也是也可以通过在-XX:+PrintFlagsFinal JVM 参数里面得到这个值。

:那看看在原始发布的java8里面运行相同的测试里,java8也依旧能够支持-XX:StringTableSize参数来兼容java7的特性。但它的主要一个区别是在java8里面原本默认池的大小增加到60013:

50000; time = 0.019 sec

100000; time = 0.009 sec

150000; time = 0.009 sec

200000; time = 0.009 sec

250000; time = 0.009 sec

300000; time = 0.009 sec

350000; time = 0.011 sec

400000; time = 0.012 sec

450000; time = 0.01 sec

500000; time = 0.013 sec

550000; time = 0.013 sec

600000; time = 0.014 sec

650000; time = 0.018 sec

700000; time = 0.015 sec

750000; time = 0.029 sec

800000; time = 0.018 sec

850000; time = 0.02 sec

900000; time = 0.017 sec

950000; time = 0.018 sec

1000000; time = 0.021 sec

《Java程序学习手册》
《Java程序学习手册》

    原文作者:盼盼
    原文地址: https://zhuanlan.zhihu.com/p/24202556
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞