提高 Java 代码性能的各种技巧

  • 时间:
  • 浏览:0

现在,调整 -XX:StringTableSize=1115003 参数来重新运行测试:

肯能你想学习Java可不也能来你是什么 群,首先是二二零,后边是一四二,最后是九零六,后边可不也能学习交流,就让资料可不也能下载。

我没办法 注意到在 intern 小于 1150 字符的字符串时的依赖状态(我认为在没办法 含高 150 个重复字符的字符串与现实数据何必 类式,全都我 1150 个字符看上去是没办法 很好的测试限制)

字符串池是使用没办法 拥有固定容量的 HashMap 每个元素含高具有相同 hash 值的字符串列表。

Java 7 中 oracle 的工程师对字符串池的逻辑做了很大的改变 — 字符串池的位置被调整到 heap 中了。这由于你再全都我会被固定的内存空间限制了。所有的字符串都保发生堆(heap)中同或者 普通对象一样,这使得你在调优应用时仅都要调整堆大小。这 个改动使得我们歌词 都都儿有足够的理由我们歌词 都都都儿重新考虑在 Java 7 中使用 String.intern()。

在 Java 6 中你是什么 参数没办法 很多帮助,肯能你仍任被限制在固定的 PermGen 内存大小中。后续的讨论将直接忽略 Java 6

你肯能期待关于 String 在 Map 中的分配 — 可不也能阅读我就让 关于 HashCode 法律措施调优的经验。

在 Java7 中,换句话说,你被限制在没办法 更大的堆内存中。这由于我就预先设置好 String 池的大小(你是什么 值取决于你的应用应用线程池池需求)。通常说来,一旦应用线程池池刚现在开始了了内存消耗,内存就让成百兆的增长,在你是什么 状态下,给没办法 拥有 1150 万字符串对象的字符串池分配 8-16M 的内存看起来是比较适合的(何必 使用1,000,000 作为 -XX:StringTaleSize 的值 – 它就让质数;使用 1,000,003代替)

Java7u40 版本扩展了字符串池的大小(这是组要的性能更新)到 150013.你是什么 值允许你在池含高高大概 11500 个独立的字符串。通常来说,这对于都要保存的数据来说肯能足够了,我就通过 -XX:+PrintFlagsFinal JVM 参数获得你是什么 值。

当 JVM 有足够内存时,手工编写的池提供了良好的性能。不过不幸的是,我的测试(保留 String.valueOf(0 < N < 1,000,000,000))保留非常短的字符串,在使用 -Xmx12150M 参数时它允许我保留月为 2.5M 的类式字符串。JVM 字符串池 (size=1,000,003)从自己面讲在 JVM 内存足够时提供了相同的性能形态,知道 JVM 字符串池含高 12.72M 的字符串并消耗掉所有内存(5倍多)。我认为,这非常值得你在你的应用中再加所有手工字符串池。

如你所想看 的,时间非常平均,全都我与 “0 到 1150万” 的表没办法 很多差别。甚至在池大小足够大的状态下,我的笔记本也能每秒再加1,000,000个字符对象。

全都有标准禁止在 Java 6 中使用 String.intern() 肯能肯能频繁使用池会市区控制,有很大的几率触发 OutOfMemoryException。Oracle Java 7 对字符串池做了全都有改进。

你都要设置没办法 更大的 -XX:StringTalbeSize 值(相比较默认的 11509 ),肯能你希望更多的使用 String.intern() — 全都我你是什么 法律措施将减慢递减到 0 (池大小)。

这篇文章的测试代码很简单,没办法 法律措施中循环创建并保留新字符串。我就测量它保留 111500 个字符串所都要的时间。最好配合 -verbose:gc JVM 参数来运行你是什么 测试,另没办法 可不也能查看垃圾挂接是哪天以及怎么才能 才能 发生的。另外最好使用 -Xmx 参数来执行堆的最大值。

现在我们歌词 都都儿都要对比 JVM 字符串池和 WeakHashMap<String, WeakReference<String>> 它可不也能用来模拟 JVM 字符串池。下面的法律措施用来替换 String.intern

这篇文章将要讨论 Java 6 中是怎么才能 才能 实现 String.intern 法律措施的,以及你是什么 法律措施在 java 7 以及 Java 8 中做了你是什么 调整。

在美好的过去所有共享的 String 对象都存储在 PermGen 中 — 堆中固定大小的偏离 主要用于存储加载的类对象和字符串池。除了明确的共享字符串,PermGen 字符串池还含高所有应用线程池池中使用过的字符串(这里要注意是使用过的字符串,肯能类肯能法律措施从未加载肯能被条用,在其中定义的任何常量就让会被加载)

现在我们歌词 都都都儿将吃的大小增加到 1150 万(精确的说是 1,000,003)

测试是在 Core i5-3317U@1.7Ghz CPU 设备上进行的。我就想看 ,它成线性增长,全都我在 JVM 字符串池含高一百万个字符串时,我仍然可不也能近似每秒 intern 11500 个字符串,这对于在内存中外理血块数据的应用应用线程池池来说太慢了。

下面是默认池大小的应用应用线程池池日志:第一列是肯能 intern 的字符串数量,第二列 intern 10,000 个字符串所有的时间(秒)

这里有没办法 测试:testStringPoolGarbageCollection 将显示 JVM 字符串池被垃圾挂接 — 检查垃圾挂接日志消息。在 Java 6 的默认 PermGen 大小配置上,你是什么 测试会失败,全都我最好增加你是什么 值,肯能更新测试法律措施,肯能使用 Java 7.

Java 6 中字符串池的最难题是它的位置 — PermGen。PermGen 的大小是固定的全都我在运行时是无法扩展的。我就使用 -XX:MaxPermSize=N 配置来调整它的大小。据我了解,对于不同的平台默认的 PermGen 大小在 32M 到 96M 之间。我就扩展它的大小,不过大小使用就让固定的。你是什么 限制都要你在使用 String.intern 时都要非常小心 — 你最好何必 使用你是什么 法律措施 intern 任何无法控制的用户输入。这是为什么我儿 在 JAVA6 中大偏离 使用手动管理 Map 来实现字符串池

字符串池(有名字符串标准化)是通过使用唯一的共享 String 对象来使用相同的值不同的地址表示字符串的过程。我就使用自己定义的 Map<String, String> (根据都要使用 weak 引用肯能 soft 引用)并使用 map 中的值作为标准值来实现你是什么 目标,肯能你也可不也能使用 JDK 提供的 String.intern()

默认的池大小是 11509 (冒出在后边提及的 bug 报告的源码中,在 Java7u40 中增加了)。在 JAVA 6 早期版本中是没办法 常量,在就让 的 java6u150 至 java6u41 中调整为可配置的。而在java 7中一刚现在开始了了全都我可不也能配置的(大概在java7u02中是可不也能配置的)。你都要指定参数 -XX:StringTableSize=N,  N 是字符串池 Map 的大小。确保它是为性能调优而预先准备的大小。

第三个测试显示内存中保留了十几个 字符串。在 Java 6 中执行都要没办法 不同的内存配置 比如: -Xmx128M 以及 -Xmx12150M (10 倍以上)。你肯能发现你是什么 值不需要影响上放去池中字符串的数量。自己面,在 Java 7 中你也能在堆中填满你的字符串。

我尝试在原始发布的 Java 8 中运行相同的测试,Java 8 仍然支持 -XX:StringTableSize 参数来兼容 Java 7 形态。主要的区别在于 Java 8 中默认的池大小增加到 150013:

可不也能想看 ,这时插入字符串的时间近似于常量(在 Map 的字符串列表中平均字符串个数不超过 10 个),下面是相同设置的结果,不过这次我们歌词 都都儿将向池中插入 11150 万个字符串(这由于 Map 中的字符串列表平均含高 1150 个字符串)

没错,在 JVM 字符串池中的所有字符串会被垃圾挂接,肯能你是什么 值在应用中没办法 任何引用。这是用于所有版本的 Java,这由于肯能 interned 的字符串在作用域外全都我没办法 任何引用 — 它肯能从 JVM 的字符串池中被垃圾挂接掉。

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

肯能被重新定位到堆中以及会被垃圾挂接,JVM 的字符串池看上去是存放字符串的大概位置,是吗?理论上是 — 违背使用的字符串会从池中挂接掉,当内控 输入没办法 字符传且池中发生时可不也能节省内存。看起来是没办法 完美的节省内存的策略?在你回答你是什么 就让 ,可不也能肯定的在等你 都要知道字符串池是怎么才能 才能 实现的。