2012-11-13 45 views
5

我写了很多东西来登录突发,并优化数据路径。我使用StringBuilder构建日志文本。什么是最有效的初始容量,内存管理是明智的,所以无论JVM如何,它都能很好地工作。目标是几乎总是避免重新分配,初始容量应该在80-100左右。但是我也想浪费尽可能少的字节,因为StringBuilder实例可能会在缓冲区中出现并浪费字节。StringBuilder最有效的初始容量大小?

我意识到这取决于JVM,但应该有一些价值,这将浪费最少的字节,无论JVM,“最不共同的分母”。我目前使用128-16,其中128是一个不错的整数,减法用于分配开销。此外,这可能被认为是“过早优化”的情况,但是由于我接下来的答案是“经验法则”数字,因为知道它在未来也会有用。

我并不期待“我最好的猜测”答案(我自己的答案已经是这样),我希望有人已经研究过这个问题,可以分享一个基于知识的答案。

+0

对这个问题的回答取决于很多事情,例如,在“StringBuilder”中存储文本的时间有多长等等。要找出的唯一方法是使用内存和/或CPU分析器进行测量。除非创建数十万个'StringBuilder'对象,否则没有理由担心几个字节。 – Jesper

+1

到目前为止最大的开销是IO的成本。除非您不打算将这些数据写入IO,否则我不会担心。 –

回答

3

那么,我最后简单地测试了一下自己,然后在评论之后再测试一些内容以获得此编辑答案。

使用JDK 1.7.0_07和测试应用程序报告VM名 “的Java的HotSpot(TM)64位服务器VM”,StringBuilder存储器使用的粒度是4个字符,在甚至4个字符增加。

答案:从内存分配的角度来看,至少在这个64位JVM上,StringBuilder的任何倍数都是同等优秀的4

通过创建具有不同初始容量的1000000个StringBuilder对象,在不同的测试程序执行中(具有相同的初始堆状态)以及在前后打印出ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed()进行测试。

打印出的堆大小也得到确认,每个StringBuilder的缓冲区实际从堆中分配的数量是8个字节的偶数倍,正如预期的那样,因为Java字符长度为2个字节。换句话说,分配具有初始容量1..4的1000000个实例需要大约8兆字节的内存(每个实例8个字节),而不是分配相同数量的初始容量为5 ... 8的实例。

+0

你介意分享你的测试程序吗? - 你如何设法以这样的粒度来确定堆的使用情况? – JimmyB

+0

我没有代码,但堆使用率在StringBuilder初始容量中增加了4个单位,然后在3个下一个大小上相同,然后再次跳到4的下一个倍数。 **但**这是4个字符,意思是8个字节,对不对?谢谢你的提问,我明天再试一次来验证这一点。 – hyde

+0

因此,您观察到1000000 x 4个字节的堆用量增加了? - 我不敢想象估计[数据结构]将占用多少字节的Java堆空间,而不是用于任何Java程序中的“char”而不是任何其他值/类型。 - 此外,无论堆的*分配*粒度如何,GC决定将内存释放回堆的粒度都是未知的,并且会影响任何度量。 - 如果您出于好奇和/或测量给定JVM的某些特性而进行测试,请继续。 - 否则,...看到我的回答上面:) – JimmyB

4

不要在这种情况下变得聪明。

我目前使用128-16,其中128是一个不错的回合数,减法是分配开销。

在Java中,这是基于对JVM内部工作原理的完全任意的假设。 Java不是C.字节对齐等绝对是而不是程序员可以或应该尝试利用的问题。

如果您知道您的字符串的(可能的)最大长度,则可以将其用于初始大小。除此之外,任何优化尝试都是徒劳的。

如果你真的知道是大量的StringBuilder S的将围绕很长的时间(这不太适合记录的概念),你真的觉得有必要去说服JVM为了节省一些字节的堆空间,你可以尝试在字符串完全构建之后使用trimToSize()。但是,再一次,只要你的字符串不会浪费兆字节,你就应该去关注应用程序中的其他问题。