提高Java代码性能的各种技巧
作者:网络转载 发布时间:[ 2015/5/7 11:18:54 ] 推荐标签:编程语言
下面针对手工池的相同测试:
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有足够内存时,手工编写的池提供了良好的性能。不过不幸的是,我的测试(保留String.valueOf(0<N<1,000,000,000))保留非常短的字符串,在使用-Xmx1280M参数时它允许我保留月为2.5M的这类字符串。JVM字符串池(size=1,000,003)从另一方面讲在JVM内存足够时提供了相同的性能特性,知道JVM字符串池包含12.72M的字符串并消耗掉所有内存(5倍多)。我认为,这非常值得你在你的应用中去掉所有手工字符串池。
在Java 7u40+以及Java 8中的String.intern()
Java7u40版本扩展了字符串池的大小(这是组要的性能更新)到60013.这个值允许你在池中包含大约30000个独立的字符串。通常来说,这对于需要保存的数据来说已经足够了,你可以通过-XX:+PrintFlagsFinal JVM参数获得这个值。
我尝试在原始发布的Java 8中运行相同的测试,Java 8仍然支持-XX:StringTableSize参数来兼容Java 7特性。主要的区别在于Java 8中默认的池大小增加到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
测试代码
这篇文章的测试代码很简单,一个方法中循环创建并保留新字符串。你可以测量它保留10000个字符串所需要的时间。好配合-verbose:gc JVM参数来运行这个测试,这样可以查看垃圾收集是何时以及如何发生的。另外好使用-Xmx参数来执行堆的大值。
这里有两个测试:testStringPoolGarbageCollection将显示JVM字符串池被垃圾收集—检查垃圾收集日志消息。在Java 6的默认PermGen大小配置上,这个测试会失败,因此好增加这个值,或者更新测试方法,或者使用Java 7.
第二个测试显示内存中保留了多少字符串。在Java 6中执行需要两个不同的内存配置比如:-Xmx128M以及-Xmx1280M(10倍以上)。你可能发现这个值不会影响放入池中字符串的数量。另一方面,在Java 7中你能够在堆中填满你的字符串。
/**
-Testing String.intern.
*
-Run this class at least with-verbose:gc JVM parameter.
*/
public class InternTest{
public static void main(String[]args){
testStringPoolGarbageCollection();
testLongLoop();
}
/**
-Use this method to see where interned strings are stored
-and how many of them can you fit for the given heap size.
*/
private static void testLongLoop()
{
test(1000*1000*1000);
//uncomment the following line to see the hand-written cache performance
//testManual(1000*1000*1000);
}
/**
-Use this method to check that not used interned strings are garbage collected.
*/
private static void testStringPoolGarbageCollection()
{
//first method call-use it as a reference
test(1000*1000);
//we are going to clean the cache here.
System.gc();
//check the memory consumption and how long does it take to intern strings
//in the second method call.
test(1000*1000);
}
private static void test(final int cnt)
{
final List<String>lst=new ArrayList<String>(100);
long start=System.currentTimeMillis();
for(int i=0;i<cnt;++i)
{
final String str="Very long test string,which tells you about something"+
"very-very important,definitely deserving to be interned#"+i;
//uncomment the following line to test dependency from string length
//final String str=Integer.toString(i);
lst.add(str.intern());
if(i%10000==0)
{
System.out.println(i+";time="+(System.currentTimeMillis()-start)/1000.0+"sec");
start=System.currentTimeMillis();
}
}
System.out.println("Total length="+lst.size());
}
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;
}
private static void testManual(final int cnt)
{
final List<String>lst=new ArrayList<String>(100);
long start=System.currentTimeMillis();
for(int i=0;i<cnt;++i)
{
final String str="Very long test string,which tells you about something"+
"very-very important,definitely deserving to be interned#"+i;
lst.add(manualIntern(str));
if(i%10000==0)
{
System.out.println(i+";manual time="+(System.currentTimeMillis()-start)/1000.0+"sec");
start=System.currentTimeMillis();
}
}
System.out.println("Total length="+lst.size());
}
}
总结
由于Java 6中使用固定的内存大小(PermGen)因此不要使用String.intern()方法
Java7和8在堆内存中实现字符串池。这以为这字符串池的内存限制等于应用程序的内存限制。
在Java 7和8中使用-XX:StringTableSize来设置字符串池Map的大小。它是固定的,因为它使用HashMap实现。近似于你应用单独的字符串个数(你希望保留的)并且设置池的大小为接近的质数并乘以2(减少碰撞的可能性)。它是的String.intern可以使用相同(固定)的时间并且在每次插入时消耗更小的内存(同样的任务,使用java WeakHashMap将消耗4-5倍的内存)。
在Java 6和7(Java7u40以前)中-XX:StringTableSize参数的值是1009。Java7u40以后这个值调整为60013(Java 8中使用相同的值)
如果你不确定字符串池的用量,参考:-XX:+PrintStringTableStatistics JVM参数,当你的应用挂掉时它告诉你字符串池的使用量信息。
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系SPASVO小编(021-61079698-8054),我们将立即处理,马上删除。
相关推荐
Java性能测试有哪些不为众人所知的原则?Java设计模式??装饰者模式谈谈Java中遍历Map的几种方法Java Web入门必知你需要理解的Java反射机制知识总结编写更好的Java单元测试的7个技巧编程常用的几种时间戳转换(java .net 数据库)适合Java开发者学习的Python入门教程Java webdriver如何获取浏览器新窗口中的元素?Java重写与重载(区别与用途)Java变量的分类与初始化JavaScript有这几种测试分类Java有哪四个核心技术?给 Java开发者的10个大数据工具和框架Java中几个常用设计模式汇总java生态圈常用技术框架、开源中间件,系统架构及经典案例等

sales@spasvo.com