提高 Java 代码性能的各种技巧Word文档格式.docx

上传人:b****2 文档编号:5072812 上传时间:2023-05-04 格式:DOCX 页数:13 大小:20.66KB
下载 相关 举报
提高 Java 代码性能的各种技巧Word文档格式.docx_第1页
第1页 / 共13页
提高 Java 代码性能的各种技巧Word文档格式.docx_第2页
第2页 / 共13页
提高 Java 代码性能的各种技巧Word文档格式.docx_第3页
第3页 / 共13页
提高 Java 代码性能的各种技巧Word文档格式.docx_第4页
第4页 / 共13页
提高 Java 代码性能的各种技巧Word文档格式.docx_第5页
第5页 / 共13页
提高 Java 代码性能的各种技巧Word文档格式.docx_第6页
第6页 / 共13页
提高 Java 代码性能的各种技巧Word文档格式.docx_第7页
第7页 / 共13页
提高 Java 代码性能的各种技巧Word文档格式.docx_第8页
第8页 / 共13页
提高 Java 代码性能的各种技巧Word文档格式.docx_第9页
第9页 / 共13页
提高 Java 代码性能的各种技巧Word文档格式.docx_第10页
第10页 / 共13页
提高 Java 代码性能的各种技巧Word文档格式.docx_第11页
第11页 / 共13页
提高 Java 代码性能的各种技巧Word文档格式.docx_第12页
第12页 / 共13页
提高 Java 代码性能的各种技巧Word文档格式.docx_第13页
第13页 / 共13页
亲,该文档总共13页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

提高 Java 代码性能的各种技巧Word文档格式.docx

《提高 Java 代码性能的各种技巧Word文档格式.docx》由会员分享,可在线阅读,更多相关《提高 Java 代码性能的各种技巧Word文档格式.docx(13页珍藏版)》请在冰点文库上搜索。

提高 Java 代码性能的各种技巧Word文档格式.docx

除了明确的共享字符串,PermGen字符串池还包含所有程序中使用过的字符串(这里要注意是使用过的字符串,如果类或者方法从未加载或者被条用,在其中定义的任何常量都不会被加载)

Java6中字符串池的最大问题是它的位置—PermGen。

PermGen的大小是固定的并且在运行时是无法扩展的。

你可以使用 

-XX:

MaxPermSize=N 

配置来调整它的大小。

据我了解,对于不同的平台默认的PermGen大小在32M到96M之间。

你可以扩展它的大小,不过大小使用都是固定的。

这个限制需要你在使用 

时需要非常小心—你最好不要使用这个方法intern任何无法控制的用户输入。

这是为什么在JAVA6中大部分使用手动管理 

Map 

来实现字符串池

Java7中的String.intern()

Java7中Oracle的工程师对字符串池的逻辑做了很大的改变—字符串池的位置被调整到heap中了。

这意味着你再也不会被固定的内存空间限制了。

所有的字符串都保存在堆(heap)中同其他普通对象一样,这使得你在调优应用时仅需要调整堆大小。

这个改动使得我们有足够的理由让我们重新考虑在Java7中使用String.intern()。

字符串池中的数据会被垃圾收集

没错,在JVM字符串池中的所有字符串会被垃圾收集,如果这些值在应用中没有任何引用。

这是用于所有版本的Java,这意味着如果 

interned的字符串在作用域外并且没有任何引用—它将会从JVM的字符串池中被垃圾收集掉。

因为被重新定位到堆中以及会被垃圾收集,JVM的字符串池看上去是存放字符串的合适位置,是吗?

理论上是—违背使用的字符串会从池中收集掉,当外部输入一个字符传且池中存在时可以节省内存。

看起来是一个完美的节省内存的策略?

在你回答这个之前,可以肯定的是你需要知道字符串池是如何实现的。

在Java6,7,8中JVM字符串池的实现

字符串池是使用一个拥有固定容量的 

HashMap 

每个元素包含具有相同hash值的字符串列表。

一些实现的细节可以从Javabug报告中获得 

默认的池大小是1009(出现在上面提及的bug报告的源码中,在Java7u40中增加了)。

在JAVA6早期版本中是一个常量,在随后的 

java6u30至java6u41中调整为可配置的。

而在java7中一开始就是可以配置的(至少在java7u02中是可以配置的)。

你需要指定参数 

StringTableSize=N, 

N是字符串池 

的大小。

确保它是为性能调优而预先准备的大小。

在Java6中这个参数没有太多帮助,因为你仍任被限制在固定的PermGen内存大小中。

后续的讨论将直接忽略Java6

Java7(直至Java7u40)

在Java7中,换句话说,你被限制在一个更大的堆内存中。

这意味着你可以预先设置好String池的大小(这个值取决于你的应用程序需求)。

通常说来,一旦程序开始内存消耗,内存都是成百兆的增长,在这种情况下,给一个拥有100万字符串对象的字符串池分配8-16M的内存看起来是比较适合的(不要使用1,000,000作为 

StringTaleSize 

的值–它不是质数;

使用 

1,000,003代替)

你可能期待关于String在Map中的分配—可以阅读我之前关于HashCode方法调优的经验。

你必须设置一个更大的 

StringTalbeSize 

值(相比较默认的1009),如果你希望更多的使用String.intern()—否则这个方法将很快递减到0(池大小)。

我没有注意到在intern小于100字符的字符串时的依赖情况(我认为在一个包含50个重复字符的字符串与现实数据并不相似,因此100个字符看上去是一个很好的测试限制)

下面是默认池大小的应用程序日志:

第一列是已经intern的字符串数量,第二列intern10,000个字符串所有的时间(秒)

0;

time=0.0sec

50000;

time=0.03sec

100000;

time=0.073sec

150000;

time=0.13sec

200000;

time=0.196sec

250000;

time=0.279sec

300000;

time=0.376sec

350000;

time=0.471sec

400000;

time=0.574sec

450000;

time=0.666sec

500000;

time=0.755sec

550000;

time=0.854sec

600000;

time=0.916sec

650000;

time=1.006sec

700000;

time=1.095sec

750000;

time=1.273sec

800000;

time=1.248sec

850000;

time=1.446sec

900000;

time=1.585sec

950000;

time=1.635sec

1000000;

time=1.913sec

测试是在Corei5-3317U@1.7GhzCPU设备上进行的。

你可以看到,它成线性增长,并且在JVM字符串池包含一百万个字符串时,我仍然可以近似每秒 

intern 

5000个字符串,这对于在内存中处理大量数据的应用程序来说太慢了。

现在,调整 

StringTableSize=100003 

参数来重新运行测试:

time=0.017sec

time=0.009sec

time=0.01sec

time=0.007sec

time=0.008sec

time=0.013sec

time=0.011sec

time=0.012sec

time=0.015sec

可以看到,这时插入字符串的时间近似于常量(在Map的字符串列表中平均字符串个数不超过10个),下面是相同设置的结果,不过这次我们将向池中插入1000万个字符串(这意味着Map中的字符串列表平均包含100个字符串)

2000000;

time=0.024sec

3000000;

time=0.028sec

4000000;

time=0.053sec

5000000;

time=0.051sec

6000000;

time=0.034sec

7000000;

time=0.041sec

8000000;

time=0.089sec

9000000;

time=0.111sec

10000000;

time=0.123sec

现在让我们将吃的大小增加到100万(精确的说是1,000,003)

time=0.005sec

time=0.004sec

如你所看到的,时间非常平均,并且与“0到100万”的表没有太大差别。

甚至在池大小足够大的情况下,我的笔记本也能每秒添加1,000,000个字符对象。

我们还需要手工管理字符串池吗?

现在我们需要对比JVM字符串池和 

WeakHashMap<

String,WeakReference<

String>

>

它可以用来模拟JVM字符串池。

下面的方法用来替换 

String.intern:

privatestaticfinalWeakHashMap<

s_manualCache=

newWeakHashMap<

(100000);

privatestaticStringmanualIntern(finalStringstr)

{

finalWeakReference<

cached=s_manualCache.get(str);

if(cached!

=null)

{

finalStringvalue=cached.get();

if(value!

returnvalue;

}

s_manualCache.put(str,newWeakReference<

(str));

returnstr;

}

下面针对手工池的相同测试:

manualtime=0.001sec

manualtime=0.03sec

manualtime=0.034sec

manualtime=0.008sec

manualtime=0.019sec

manualtime=0.011sec

manualtime=0.027sec

manualtime=0.009sec

manualtime=0.007sec

当JVM有足够内存时,手工编写的池提供了良好的性能。

不过不幸的是,我的测试(保留 

String.valueOf(0<

N<

1,000,000,000))保留非常短的字符串,在使用 

-Xmx1280M 

参数时它允许我保留月为2.5M的这类字符串。

JVM字符串池(size=1,000,003)从另一方面讲在JVM内存足够时提供了相同的性能特性,知道JVM字符串池包含12.72M的字符串并消耗掉所有内存(5倍多)。

我认为,这非常值得你在你的应用中去掉所有手工字符串池。

在Java7u40+以及Java8中的String.intern()

Java7u40版本扩展了字符串池的大小(这是组要的性能更新)到60013.这个值允许你在池中包含大约30000个独立的字符串。

通常来说,这对于需要保存的数据来说已经足够了,你可以通过 

+PrintFlagsFinal 

JVM参数获得这个值。

我尝试在原始发布的Java8中运行相同的测试,Java8仍然支持 

StringTableSize 

参数来兼容Java7特性。

主要的区别在于Java8中默认的池大小增加到60013:

time=0.019sec

time=0.014sec

time=0.018sec

time=0.029sec

time=0.02sec

time=0.021sec

测试代码

这篇文章的测试代码很简单,一个方法中循环创建并保留新字符串。

你可以测量它保留10000个字符串所需要的时间。

最好配合 

-verbose:

gc 

JVM参数来运行这个测试,这样可以查看垃圾收集是何时以及如何发生的。

另外最好使用 

-Xmx 

参数来执行堆的最大值。

这里有两个测试:

testStringPoolGarbageCollection 

将显示JVM字符串池被垃圾收集—检查垃圾收集日志消息。

在Java6的默认PermGen大小配置上,这个测试会失败,因此最好增加这个值,或者更新测试方法,或者使用Java7.

第二个测试显示内存中保留了多少字符串。

在Java6中执行需要两个不同的内存配置比如:

-Xmx128M 

以及 

(10倍以上)。

你可能发现这个值不会影响放入池中字符串的数量。

另一方面,在Java7中你能够在堆中填满你的字符串。

/**

-TestingString.intern.

*

-Runthisclassatleastwith-verbose:

gcJVMparameter.

*/

publicclassInternTest{

publicstaticvoidmain(String[]args){

testStringPoolGarbageCollection();

testLongLoop();

/**

-Usethismethodtoseewhereinternedstringsarestored

-andhowmanyofthemcanyoufitforthegivenheapsize.

privatestaticvoidtestLongLoop()

test(1000*1000*1000);

//uncommentthefollowinglinetoseethehand-writtencacheperformance

//testManual(1000*1000*1000);

-Usethismethodtocheckthatnotusedinternedstringsaregarbagecollected.

privatestaticvoidtestStringPoolGarbageCollection()

//firstmethodcall-useitasareference

test(1000*1000);

//wearegoingtocleanthecachehere.

System.gc();

//checkthememoryconsumptionandhowlongdoesittaketointernstrings

//inthesecondmethodcall.

privatestaticvoidtest(finalintcnt)

finalList<

lst=newArrayList<

(100);

longstart=System.currentTimeMillis();

for(inti=0;

i<

cnt;

++i)

finalStringstr="

Verylongteststring,whichtellsyouaboutsomething"

+

"

very-veryimportant,definitelydeservingtobeinterned#"

+i;

//uncommentthefollowinglinetotestdependencyfromstringlength

//finalStringstr=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("

Totallength="

+lst.size());

privatestaticfinalWeakHashMap<

s_manualCache=

privatestaticStringmanualIntern(finalStringstr)

privatestaticvoidtestManual(finalintcnt)

lst.add(manualIntern(str));

manualtime="

+

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 党团工作 > 入党转正申请

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2