本文介绍了积极的垃圾收集器策略的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在运行一个创建并忘记大量对象的应用程序,现有的长期存在的对象的数量确实增长缓慢,但是与寿命短的对象相比,这很少.这是具有高可用性要求的桌面应用程序,需要每天24小时开机.大部分工作都是在单个线程上完成的,该线程将使用它可以使用的所有CPU.

I am running an application that creates and forgets large amounts of objects, the amount of long existing objects does grow slowly, but this is very little compared to short lived objects. This is a desktop application with high availability requirements, it needs to be turned on 24 hours per day. Most of the work is done on a single thread, this thread will just use all CPU it can get its hands.

过去,我们在重负载下看到以下内容:随着垃圾收集器收集的内存少于新分配的内存量,使用的堆空间缓慢增加,使用的堆大小缓慢增长并最终接近指定的最大堆.届时,垃圾收集器将投入大量资源并开始使用大量资源,以防止超出最大堆大小.这会使应用程序变慢(很慢,慢10倍),并且在大多数情况下,GC将在几分钟后成功清理垃圾或失败并抛出OutOfMemoryException,这两个都是不可接受的.

In the past we have seen the following under heavy load:The used heap space slowly goes up as the garbage collector collects less than the amount of memory newly allocated, the used heap size slowly grows and eventually comes near the specified max heap. At that point the garbage collector will kick in heavily and start using a huge amount of resources to prevent going over the max heap size. This slows the application down (easily 10x as slow) and at this point most of times the GC will succeed to clean up the garbage after a few minutes or fail and throw an OutOfMemoryException, both of them are not really acceptable.

所使用的硬件是四核处理器,至少有4GB的内存运行64位Linux,我们可以根据需要使用所有这些处理器.当前,该应用程序大量使用单个内核,而该内核大部分时间都在运行单个内核/线程.其他内核大部分处于空闲状态,可以用于垃圾回收.

The hardware used is a quad core processor with at least 4GB of memory running 64 bit Linux, all of that we can use if needed. Currently the application is heavily using a single core, which is using most of its time running a single core/thread. The other cores are mostly idle and could be used for garbage collection.

我觉得垃圾收集器应该在内存耗尽之前尽早更积极地进行收集.我们的应用程序没有任何吞吐量问题,低暂停时间要求比吞吐量更重要,但远没有达到最大堆大小重要.如果单个繁忙线程仅以当前速度的75%运行,这是可以接受的,只要这意味着垃圾收集器可以跟上创建的步伐即可.简而言之,性能的稳步下降要好于我们现在看到的突然下降.

I have a feeling the garbage collector should be collecting more aggressively at an early stage, well before it runs out of memory. Our application does not have any throughput issues, low pause time requirements are a bit more important than throughput, but far less important than not getting near the max heap size. It is acceptable if the single busy thread runs at only 75% of the current speed, as long as it means the garbage collector can keep up with the creation. So in short, a steady decrease of performance is better than the sudden drop we see now.

我已阅读 Java SE 6 HotSpot [tm ]虚拟机垃圾收集调优,这意味着我很了解这些选项,但是由于我的要求与本文中讨论的要求有所不同,因此我仍然很难选择正确的设置.

I have read Java SE 6 HotSpot[tm] Virtual Machine Garbage Collection Tuning thoroughly, which means I understand the options well, however I still find it hard to chose the right settings as my requirements are a bit different from what is discussed in the paper.

当前,我正在使用带有-XX:GCTimeRatio=4选项的ParallelGC.这比时间比率的默认设置要好一些,但是我觉得该设置允许GC运行的次数更多.

Currently I am using the ParallelGC with the option -XX:GCTimeRatio=4. This works a bit better than the default setting for time ratio, but I have a feeling the GC is allowed to run more by that setting than it does.

为了进行监视,我主要使用jconsole和jvisualvm.

For monitoring I am using jconsole and jvisualvm mostly.

我想知道您针对上述情况建议使用哪些垃圾收集选项.另外,我可以查看哪个GC调试输出以更好地理解瓶颈.

I would like to know what garbage collection options you recommend for the above situation. Also which GC debug output can I look at to understand the bottle neck better.

我知道这里有一个很好的选择,那就是创建更少的垃圾,这是我们真正考虑的问题,但是我想知道我们如何通过GC调整来解决此问题,因为这可以使我们更轻松地完成工作并进一步推广比更改大量源代码要快.另外,我运行了不同的内存探查器,并且我了解了垃圾的用途,并且据我所知,垃圾由可以收集的对象组成.

I understand a very good option here is to create less garbage, this is something we are really considering, however I would like to know how we can tackle this with GC tuning, as that is something we can do much more easily and roll out more quickly than changing large amounts of the source code. Also I have ran the different memory profilers and I understand what the garbage is used by, and there by I know it consists of objects that could be collected.

我正在使用:

java version "1.6.0_27-ea"
Java(TM) SE Runtime Environment (build 1.6.0_27-ea-b03)
Java HotSpot(TM) 64-Bit Server VM (build 20.2-b03, mixed mode)

具有JVM参数:

-Xmx1024M and -XX:GCTimeRatio=4 

编辑以回复Matts评论:大多数内存(和cpu)都用于构造代表当前情况的对象.随着情况的迅速变化,其中一些将立即被丢弃,如果一段时间内没有任何更新,则另一些将具有中等的使用寿命.

Edit in reply to Matts comments:Most memory (and cpu) goes towards constructing objects that represent the current situation. Some of these will be discarded right away as the situation changes rapidly, some others will have a medium life time if no updates come in for a while.

推荐答案

您没有提到正在运行的JVM的哪个内部版本,这是至关重要的信息.您也没有提及该应用程序可以运行多长时间(例如,工作时间是一天吗?一周?还是更少?)

You don't mention which build of the JVM you're running, this is crucial info. You also don't mention how long the app tends to run for (e.g. is it for the length of a working day? a week? less?)

其他几点

  1. 如果您由于分配的速度快于年轻一代被扫地的速度而不断泄漏对象到终身制,那么您的世代大小将不正确.您需要对应用程序的行为进行一些适当的分析,以便能够正确调整它们的大小,您可以为此使用visualgc.
  2. 吞吐量收集器旨在接受单个较大的停顿,而不是许多较小的停顿,其好处是它是紧凑的收集器,并且可以实现更高的总吞吐率
  3. CMS的存在是为了服务频谱的另一端,即,更多的暂停更少,但总吞吐量却更低.不利的一面是它没有压缩,所以碎片可能是一个问题.碎片问题在6u26中得到了改进,因此,如果您不在该版本上,则可能是升级时间.请注意,您提到的流血到长期使用"效应加剧了碎片化问题,并且在一定的时间内,这将导致升级失败(也就是计划外的完整gc和关联的STW暂停).我之前已经在此问题上写过有关此问题的答案.
  1. If you are continually leaking objects into tenured because you're allocating at a rate faster than your young gen can be swept then your generations are incorrectly sized. You will need to do some proper analysis of the behaviour of your app to be able to size them correctly, you can use visualgc for this.
  2. the throughput collector is designed to accept a single, large pause as opposed to many smaller pauses, the benefit is it is a compacting collector and it enables higher total throughput
  3. CMS exists to serve the other end of the spectrum, i.e. many more much much smaller pauses but lower total throughput. The downside is it is not compacting so fragmentation can be a problem. The fragmentation issue was improved in 6u26 so if you're not on that build then it may be upgrade time. Note that the "bleeding into tenured" effect you have remarked on exacerbates the fragmentation issue and, given time, this will lead to promotion failures (aka unscheduled full gc and associates STW pause). I have previously written an answer about this on this question
  1. 如果您正在运行具有> 4GB RAM的64位JVM和最近使用的JVM,请确保您-XX:+UseCompressedOops,否则您只是在浪费空间,因为64位JVM占用的32位JVM的空间约为1.5倍.没有它的话,工作量是相同的(如果不是,请升级以获取更多的RAM)
  1. If you're running a 64bit JVM with >4GB RAM and a recent enough JVM, make sure you -XX:+UseCompressedOops otherwise you're simply wasting space as a 64bit JVM occupies ~1.5x the space of a 32bit JVM for the same workload without it (and if you're not, upgrade to get access to more RAM)

您可能还想阅读我写过的另一个答案这个主题,它可以用来确定您的幸存者空间&适当地伊甸园.基本上,您想要实现的是;

You may also want to read another answer I've written on this subject which goes into sizing your survivor spaces & eden appropriately. Basically what you want to achieve is;

  • 变大到不经常收集的程度
  • 幸存者空间的大小与保有权期限相匹配
  • 设置使用期限阈值,以尽可能确保只有真正长寿的对象才能使用该期限

因此,假设您有一个6G堆,您可能会做类似5G eden + 16M幸存者空间+任期阈值1的事情.

Therefore say you had a 6G heap, you might do something like 5G eden + 16M survivor spaces + a tenuring threshold of 1.

基本过程是

  1. 分配到伊甸园
  2. 伊甸园充满
  3. 活物冲入幸存者空间
  4. 幸存者空间中的活动对象要么复制到太空中,要么升级为保有期限(取决于保有期限阈值和可用空间,以及是否多次将它们从1复制到另一个)
  5. 伊甸园里剩下的一切都被扫走了

因此,给定空间以适合应用程序的分配配置文件的大小,完全有可能对系统进行配置,使其能够很好地处理负载.一些注意事项;

Therefore, given spaces appropriately sized for your application's allocation profile, it's perfectly possible to configure the system such that it handles the load nicely. A few caveats to this;

  1. 您需要一些长时间运行的测试才能正确执行此操作(例如,可能需要几天时间才能解决CMS碎片问题)
  2. 您需要多次进行每次测试才能获得良好的结果
  3. 您需要在GC配置中一次更改1项
  4. 您需要能够向应用程序呈现合理的可重复工作量,否则很难客观地比较不同测试运行的结果
  5. 如果工作负载不可预测并且具有巨大的高峰/低谷,这将很难可靠地完成

要点1-3表示要花很多时间才能正确.另一方面,您也许可以快速使v变得足够好,这取决于您的肛门状况!

Points 1-3 mean this can take ages to get right. On the other hand you may be able to make it good enough v quickly, it depends how anal you are!

最后,与彼得·劳里(Peter Lawrey)的观点相呼应,如果您对对象分配的要求非常严格,则可以省去很多麻烦(尽管引入了其他麻烦).

Finally, echoing Peter Lawrey's point, you can save a lot of bother (albeit introducing some other bother) if you are really rigorous about object allocation.

这篇关于积极的垃圾收集器策略的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-18 07:03