通过GC调优,我成功地获得了实时Java应用程序的性能并避免了可识别的GC暂停。但是,这可以容纳大约20 GB的堆空间。
硬件成本的降低使得100GB的RAM机器价格实惠。但是,由于GC暂停,仍然使用Java,更高的堆大小(如50 GB)会让你经常陷入噩梦。
我知道有像堆外和分布式堆这样的选项。但是,堆外具有se / derial的缺点,并且手上的分布式堆增加了维护成本。此外,在分布式堆中,您实际上并没有充分利用RAM(比如说64 GB),而这些日子正变得越来越普遍。
因此,为了充分利用RAM的潜力,Java应用程序的垂直扩展有哪些好的解决方案?
答案 0 :(得分:5)
我正在处理一个名为Banana的原始集合库。香蕉解决了这些问题。 它很快就支持LinkedLists,HashMaps和其他可能的数据结构,而不需要保留N个对象的开销。基本上 - 整个存储可以在int []数组(或多个)中。
虽然我还没有正式发布它,但大部分都经过了很好的测试,我已经在144GB内存的服务器上成功运行它,在没有任何GC暂停的情况下保持快速一致的性能。
查看this哈希映射基准,了解如何使用Banana存储数据以及它如何垂直扩展。
https://github.com/omry/banana/wiki/Long-to-fixed-size-object-benchmark
有关详细信息,请参阅wiki。
答案 1 :(得分:0)
垃圾收集时间是堆中活动对象数量的函数。这意味着如果您的分配率非常高但活动对象的数量总是很少,您可以使用尽可能多的RAM而不会出现明显的暂停。
对于Troughput收集器(-XX:+UseParallelOldGC
),此声明尤其如此。
如果您使用CMS,则可能需要检查其增量模式(-XX:+CMSIncrementalMode
)。它将触发较小的CMS周期来清理堆的一小部分,同时利用硬件并且不会出现STW暂停。
如果CMS不是一个选项,那么你应该看看G1(-XX:+UseG1GC
),这是一个专为大堆设计的新垃圾收集器。原则是:在每个循环中仅清理堆的几个区域,但要确保这些区域包含大量死对象(快速增益)。
希望有所帮助!
来源:
答案 2 :(得分:0)
我做了一些关于扩展JVM堆大小的研究。您可以阅读更多详细信息here和here。
主要结论:年轻的GC暂停有两个组件恒定且可变。
常量组件取决于旧空间大小,对于64GiB,我预计它为80ms-120ms(取决于硬件)。
变量组件取决于年轻空间中对象的存活(因此它从集合变为集合)。它确实是应用程序特定的,但您通常可以通过减少年轻空间来减少它(以更频繁的暂停为代价)。
对于4 GiB的年轻空间,你有500毫秒。假设变量分量为400ms,如果将年轻空间减少到1 GiB,则应该有〜200ms的暂停(但每秒2次)。
另一种方法是使用更多的CPU内核,年轻的GC并行extremely well。
答案 3 :(得分:0)
G1和Shenandoah(实验性)垃圾收集器为Java应用程序提供了极大的弹性和解锁垂直扩展,可根据当前负载优化内存使用情况。更具体的工作原理在文章Minimize Java Memory Usage with the Right Garbage Collector