java 激进的垃圾收集器策略
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7980177/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Aggressive garbage collector strategy
提问by Thirler
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.
我正在运行一个创建和忘记大量对象的应用程序,长期存在的对象的数量确实增长缓慢,但与短期对象相比,这很少。这是一个高可用性要求的桌面应用程序,它需要每天 24 小时开启。大多数工作是在单个线程上完成的,该线程将只使用它可以获得的所有 CPU。
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.
过去我们在重负载下看到过以下情况:当垃圾收集器收集的内存少于新分配的内存量时,已使用的堆空间缓慢增加,已使用的堆大小缓慢增长并最终接近指定的最大堆。那时垃圾收集器将大量启动并开始使用大量资源来防止超过最大堆大小。这会减慢应用程序的速度(很容易慢 10 倍),此时大多数情况下 GC 会在几分钟后成功清理垃圾或失败并抛出OutOfMemoryException
,这两种情况都不是真的可以接受。
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.
使用的硬件是一个四核处理器,至少有 4GB 内存运行 64 位 Linux,如果需要,我们可以使用所有这些。目前,该应用程序大量使用单核,大部分时间都在运行单核/线程。其他核心大多空闲,可用于垃圾收集。
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.
我有一种感觉,垃圾收集器应该在早期阶段更积极地收集,早在它耗尽内存之前。我们的应用程序没有任何吞吐量问题,低暂停时间要求比吞吐量重要一点,但远不如不接近最大堆大小重要。如果单忙线程只以当前速度的 75% 运行是可以接受的,只要这意味着垃圾收集器可以跟上创建。所以简而言之,性能的稳步下降比我们现在看到的突然下降要好。
I have read Java SE 6 HotSpot[tm] Virtual Machine Garbage Collection Tuningthoroughly, 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.
我已经彻底阅读了Java SE 6 HotSpot[tm] Virtual Machine Garbage Collection Tuning,这意味着我很了解这些选项,但是我仍然发现很难选择正确的设置,因为我的要求与论文中讨论的有点不同.
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.
目前我正在使用带有选项的 ParallelGC -XX:GCTimeRatio=4
。这比时间比率的默认设置要好一些,但我觉得该设置允许 GC 运行的次数比它实际运行的次数要多。
For monitoring I am using jconsole and jvisualvm mostly.
为了监控,我主要使用 jconsole 和 jvisualvm。
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 调试输出以更好地理解瓶颈。
EDIT: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.
编辑:我知道这里有一个很好的选择是减少垃圾,这是我们真正考虑的事情,但是我想知道我们如何通过 GC 调整来解决这个问题,因为这是我们可以更轻松地完成并滚动的事情比更改大量源代码更快。此外,我还运行了不同的内存分析器,我了解垃圾的用途,因此我知道它由可以收集的对象组成。
I am using:
我在用:
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)
With JVM parameters:
使用 JVM 参数:
-Xmx1024M and -XX:GCTimeRatio=4
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.
编辑回复 Matts 评论:大多数内存(和 cpu)用于构建代表当前情况的对象。随着情况的迅速变化,其中一些将立即被丢弃,如果一段时间没有更新,其他一些将具有中等生命周期。
采纳答案by Matt
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?)
您没有提到您正在运行的 JVM 版本,这是至关重要的信息。你也没有提到应用程序往往运行多长时间(例如它是一个工作日的长度?一周?更少?)
A few other points
其他几点
- 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.
- 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
- 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
- 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)
- If you're running a 64bit JVM with >4GB RAM and a recent enough JVM, make sure you
- 如果您不断地将对象泄漏到tenured,因为您分配的速度比您的年轻代可以清除的速度快,那么您的代大小就不正确。您需要对应用程序的行为进行一些适当的分析才能正确调整它们的大小,您可以为此使用 visualgc。
- 吞吐量收集器旨在接受单个大暂停而不是许多较小的暂停,好处是它是一个压缩收集器,并且可以实现更高的总吞吐量
- CMS 的存在是为了服务于频谱的另一端,即更多、更小的停顿但更低的总吞吐量。缺点是它没有压缩,所以碎片可能是一个问题。碎片问题在 6u26 中得到了改进,所以如果你不在那个版本上,那么可能是升级时间了。请注意,您所评论的“渗入终身”效应会加剧碎片问题,并且随着时间的推移,这将导致升级失败(也就是计划外的完整 gc 和关联 STW 暂停)。我以前在这个问题上写过一个关于这个的答案
- 如果您运行的是 64 位 JVM 且内存大于 4GB 并且有一个足够新的 JVM,请确保
-XX:+UseCompressedOops
否则您只是在浪费空间,因为 64 位 JVM 在没有它的情况下对于相同的工作负载占用的空间是 32 位 JVM 的 1.5 倍(并且如果不是,请升级以访问更多 RAM)
- 如果您运行的是 64 位 JVM 且内存大于 4GB 并且有一个足够新的 JVM,请确保
You may also want to read another answer I've written on this subjectwhich goes into sizing your survivor spaces & eden appropriately. Basically what you want to achieve is;
您可能还想阅读我在这个主题上写的另一个答案,其中涉及适当地调整您的幸存者空间和伊甸园的大小。基本上你想要实现的是;
- eden big enough that it is not collected too often
- survivor spaces sized to match the tenuring threshold
- a tenuring threshold set to ensure, as much as possible, that only truly long lived objects make it into tenured
- 伊甸园足够大,不会经常收集
- 幸存者空间的大小与任期阈值相匹配
- 设置一个使用期限阈值,以尽可能确保只有真正长寿的对象才能使其成为使用期限
Therefore say you had a 6G heap, you might do something like 5G eden + 16M survivor spaces + a tenuring threshold of 1.
因此,假设你有一个 6G 的堆,你可能会做一些类似 5G 伊甸园 + 16M 幸存者空间 + 任期阈值 1 的事情。
The basic process is
基本流程是
- allocate into eden
- eden fills up
- live objects swept into the to survivor space
- live objects in from survivor space either copied to the to space or promoted to tenured (depending on tenuring threshold & space available & no of times they've been copied from 1 to the other)
- anything left in eden is swept away
- 分配到伊甸园
- 伊甸园填满
- 活体进入幸存者空间
- 幸存者空间中的存活对象要么复制到空间,要么提升为终身(取决于任期阈值和可用空间以及它们从 1 复制到另一个的次数)
- 留在伊甸园的任何东西都被扫除
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;
因此,给定适合应用程序分配配置文件大小的空间,完全有可能配置系统以便它很好地处理负载。对此有一些警告;
- you need some long running tests to do this properly (e.g. can take days to hit the CMS fragmentation problem)
- you need to do each test a few times to get good results
- you need to change 1 thing at a time in the GC config
- you need to be able to present a reasonably repeatable workload to the app otherwise it will be difficult to objectively compare results from different test runs
- this will get really hard to do reliably if the workload is unpredictable and has massive peaks/troughs
- 您需要一些长时间运行的测试才能正确执行此操作(例如可能需要数天才能解决 CMS 碎片问题)
- 你需要做每个测试几次才能得到好的结果
- 您需要在 GC 配置中一次更改 1 件事
- 您需要能够向应用程序呈现合理可重复的工作负载,否则将很难客观地比较不同测试运行的结果
- 如果工作负载不可预测并且有大量的波峰/波谷,这将很难可靠地完成
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!
第 1-3 点意味着这可能需要很长时间才能正确。另一方面,你可以很快地让它足够好,这取决于你的肛门有多好!
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.
最后,与 Peter Lawrey 的观点相呼应,如果您对对象分配非常严格,则可以省去很多麻烦(尽管会引入一些其他麻烦)。
回答by Ravindra babu
The G1GC
algorithm, which has been introduced with stable Java 1.7
is doing well. You have to just specify maximum pause time you want to live with in your application. JVM will take care of all other things for you.
G1GC
引入了stable的算法运行Java 1.7
良好。您只需指定您希望在应用程序中使用的最大暂停时间。JVM 会为您处理所有其他事情。
Key parameters:
关键参数:
-XX:+UseG1GC -XX:MaxGCPauseMillis=1000
There are some more parameters to be configured. If you are using 4 GB RAM, configure region size as 4 GB/2048 blocks, which is roughly 2 MB
还有一些参数需要配置。如果您使用的是 4 GB RAM,请将区域大小配置为 4 GB/2048 块,大约为 2 MB
-XX:G1HeapRegionSize=2
If you have 8 core CPU, fine tune two more parameters
如果你有 8 核 CPU,再微调两个参数
-XX:ParallelGCThreads=4 -XX:ConcGCThreads=2
Apart from these parameters, leave other parameters values to default like
除了这些参数之外,将其他参数值保留为默认值,例如
-XX:TargetSurvivorRatio
etc.
-XX:TargetSurvivorRatio
等等。
Have a look at oracle websitefor more details about G1GC
.
查看oracle 网站了解有关G1GC
.
-XX:G1HeapRegionSize=n
Sets the size of a G1 region. The value will be a power of two and can range from 1MB to 32MB. The goal is to have around 2048 regions based on the minimum Java heap size.
设置 G1 区域的大小。该值将是 2 的幂,范围从 1MB 到 32MB。目标是基于最小 Java 堆大小拥有大约 2048 个区域。
-XX:MaxGCPauseMillis=200
Sets a target value for desired maximum pause time. The default value is 200 milliseconds. The specified value does not adapt to your heap size.
为所需的最大暂停时间设置目标值。默认值为 200 毫秒。指定的值不适应您的堆大小。
-XX:ParallelGCThreads=n
Sets the value of the STW worker threads. Sets the value of n to the number of logical processors. The value of n is the same as the number of logical processors up to a value of 8.
设置 STW 工作线程的值。将 n 的值设置为逻辑处理器的数量。n 的值与逻辑处理器的数量相同,最多为 8。
If there are more than eight logical processors, sets the value of n to approximately 5/8 of the logical processors. This works in most cases except for larger SPARC systems where the value of n can be approximately 5/16 of the logical processors.
如果有八个以上的逻辑处理器,请将 n 的值设置为逻辑处理器的大约 5/8。这适用于大多数情况,但较大的 SPARC 系统除外,其中 n 的值大约为逻辑处理器的 5/16。
-XX:ConcGCThreads=n
Recommendationsfrom oracle:
oracle的建议:
When you evaluate and fine-tune G1 GC, keep the following recommendations in mind:
在评估和微调 G1 GC 时,请记住以下建议:
Young Generation Size: Avoid explicitly setting young generation size with the
-Xmn
option or any or other related option such as-XX:NewRatio
.Fixing the size of the young generation overrides the target pause-time goal
.Pause Time Goals:When you evaluate or tune any garbage collection, there is always a latency versus throughput trade-off. The G1 GC is an incremental garbage collector with uniform pauses, but also more overhead on the application threads.
The throughput goal for the G1 GC is 90 percent application time and 10 percent garbage collection time
.
年轻代大小:避免使用
-Xmn
选项或任何或其他相关选项(例如-XX:NewRatio
.Fixing the size of the young generation overrides the target pause-time goal
.暂停时间目标:当您评估或调整任何垃圾收集时,总是存在延迟与吞吐量的权衡。G1 GC 是一个增量垃圾收集器,具有统一的暂停,但在应用程序线程上也有更多的开销。
The throughput goal for the G1 GC is 90 percent application time and 10 percent garbage collection time
.
Recently I have replaced CMS with G1GC algorithm for 4 GB heap with almost equal division of young gen & old gen. I set the MaxGCPause
Time and results are awesome.
最近我用 G1GC 算法替换了 CMS,用于 4 GB 堆,并且年轻代和老代几乎相等。我设置了MaxGCPause
时间,结果很棒。
回答by Peter Lawrey
You can try reducing the new size. This will case it to make more, smaller collections. However it can cause these short lived objects to be passed into tenured space. On the other hand you can try increasing the NewSize which means less objects will pass out of the young generation.
您可以尝试减小新尺寸。这将使它制作更多、更小的集合。然而,它可能导致这些短期存在的对象被传递到永久空间。另一方面,您可以尝试增加 NewSize,这意味着更少的对象将从年轻代中传递出去。
My preference however is to create less garbage and the GC will behave in a more consistent manner. Instead of creating objects freely, try re-using them or recycling objects. You have to be careful this this doesn't cause more trouble than its worth, but you can reduce the amount of garbage created significantly in some applications. I suggest using a memory profiler e.g. YourKit to help you identify the biggest hitters.
然而,我的偏好是创建更少的垃圾,GC 将以更一致的方式运行。不要随意创建对象,而是尝试重新使用它们或回收对象。您必须小心,这不会造成比其价值更多的麻烦,但是您可以显着减少在某些应用程序中创建的垃圾量。我建议使用内存分析器(例如 YourKit)来帮助您识别最大的击球手。
An extreme case is to create so little garbage it doesn't collect all day (even minor collections). It possible for a server side application (but may not be possible for a GUI application)
一个极端的情况是创建的垃圾很少,不会整天收集(即使是小收集)。服务器端应用程序可能(但 GUI 应用程序可能不可能)
回答by jtoberon
The first VM options I'd try are increasing the NewSize
and MaxNewSize
and using one of the parallel GC algorithms (try UseConcMarkSweepGC which is designed to "keep garbage collection pauses short").
第一VM选项我想尝试的增加NewSize
和MaxNewSize
使用的并行GC算法一个(试行UseConcMarkSweepGC,其目的是“保持垃圾收集暂停短”)。
To confirm that the pauses you're seeing are due to GC, turn on verbose GC logging (-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
). More info about how to read these logs is available online.
要确认您看到的暂停是由 GC 引起的,请打开详细 GC 日志记录 ( -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
)。有关如何阅读这些日志的更多信息可在线获取。
To understand the bottleneck, run the app in a profiler. Take a heap snapshot. Then, let the app do its thing for a while. Take another heap snapshot. In order to see what's taking up all the space, look for whatever there are a lot more of after the second heap snapshot. Visual VM can do this, but also consider MAT.
要了解瓶颈,请在分析器中运行该应用程序。拍摄堆快照。然后,让应用程序运行一段时间。拍摄另一个堆快照。为了查看什么占用了所有空间,请在第二个堆快照之后查找更多空间。Visual VM 可以做到这一点,但也可以考虑MAT。
Alternatively, consider using -XX:+HeapDumpOnOutOfMemoryError
so that you get a snapshot of the real problem, and you don't have to reproduce it in another environment. The heap that's saved can be analyzed with the same tools -- MAT etc..
或者,考虑使用,-XX:+HeapDumpOnOutOfMemoryError
以便您获得实际问题的快照,而不必在其他环境中重现它。可以使用相同的工具(MAT 等)分析保存的堆。
However, you may be getting an OutOfMemoryException
either because you have a memory leak or because you're running with too small a max heap size. The verbose GC logging should help you answer both of these questions.
但是,您可能会OutOfMemoryException
因为内存泄漏或运行时最大堆大小太小而得到 。详细的 GC 日志记录应该可以帮助您回答这两个问题。