Java 是什么导致 JVM 进行重大垃圾收集?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22249869/
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
What causes the JVM to do a major garbage collection?
提问by chris
I have a Java app which shows different GC behaviors in different environments. In one environment, the heap usage graph is a slow sawtooth with major GCs every 10 hours or so, only when the heap is >90% full. In another environment, the JVM does major GCs every hour on the dot (the heap is normally between 10% and 30% at these times).
我有一个 Java 应用程序,它在不同的环境中显示不同的 GC 行为。在一个环境中,堆使用图是一个缓慢的锯齿波,每 10 小时左右有一次主要 GC,仅当堆已满 90% 以上时。在另一个环境中,JVM 每小时在点上执行一次主要 GC(此时堆通常在 10% 到 30% 之间)。
My question is, what are the factors which cause the JVM to decide to do a major GC?
我的问题是,导致 JVM 决定进行主要 GC 的因素是什么?
Obviously it collects when the heap is nearly full, but there is some other cause at play which I am guessing is related to an hourly scheduled task within my app (although there is no spike in memory usage at this time).
显然,它在堆快满时收集,但还有其他一些原因在起作用,我猜这与我的应用程序中的每小时计划任务有关(尽管此时内存使用量没有激增)。
I assume GC behaviour depends heavily on the JVM; I am using:
我认为 GC 行为在很大程度上取决于 JVM;我在用:
- Java HotSpot(TM) 64-Bit Server VM 1.7.0_21 Oracle Corporation
- No specific GC options, so using the default settings for 64-bit server (PS MarkSweep and PS Scavenge)
- Java HotSpot(TM) 64 位服务器 VM 1.7.0_21 Oracle Corporation
- 没有特定的 GC 选项,因此使用 64 位服务器的默认设置(PS MarkSweep 和 PS Scavenge)
Other info:
其他信息:
- This is a web app running in Tomcat 6.
- Perm gen hovers around 10% in both environments.
- The environment with the sawtooth behaviour has 7Gb max heap, the other has 14Gb.
- 这是一个在 Tomcat 6 中运行的 Web 应用程序。
- Perm gen 在这两种环境中都徘徊在 10% 左右。
- 具有锯齿行为的环境有 7Gb 最大堆,另一个有 14Gb。
Please, no guesswork. The JVM must have rules for deciding when to perform a major GC, and these rules must be encoded deep in the source somewhere. If anyone knows what they are, or where they are documented, please share!
请不要猜测。JVM 必须有决定何时执行主要 GC 的规则,并且这些规则必须在源代码深处的某个地方进行编码。如果有人知道它们是什么,或者它们记录在哪里,请分享!
采纳答案by andrewdotn
Garbage collection is a pretty complicated topic, and while you could learn all the details about this, I think what's happening in your case is pretty simple.
垃圾收集是一个非常复杂的主题,虽然您可以了解有关此的所有详细信息,但我认为您的案例中发生的事情非常简单。
Sun's Garbage Collection Tuning guide, under the “Explicit Garbage Collection” heading, warns:
Sun 的垃圾收集调优指南,在“显式垃圾收集”标题下,警告:
applications can interact with garbage collection … by invoking full garbage collections explicitly … This can force a major collection to be done when it may not be necessary … One of the most commonly encountered uses of explicit garbage collection occurs with RMI … RMI forces full collections periodically
应用程序可以与垃圾收集交互……通过显式调用完全垃圾收集……这可以在可能不需要时强制完成主要收集……显式垃圾收集最常见的用法之一发生在 RMI 中……RMI 定期强制执行完全收集
That guide says that the default time between garbage collections is one minute, but the sun.rmi Properties reference, under sun.rmi.dgc.server.gcIntervalsays:
该指南说垃圾收集之间的默认时间是一分钟,但是sun.rmi 属性参考,下面sun.rmi.dgc.server.gcInterval说:
The default value is 3600000 milliseconds (one hour).
默认值为 3600000 毫秒(一小时)。
If you're seeing major collections every hour in one application but not another, it's probably because the application is using RMI, possibly only internally, and you haven't added -XX:+DisableExplicitGCto the startup flags.
如果您每小时在一个应用程序中看到主要集合,而在另一个应用程序中没有看到,这可能是因为该应用程序正在使用 RMI,可能仅在内部使用,并且您没有添加-XX:+DisableExplicitGC到启动标志。
Disable explicit GC, or test this hypothesis by setting -Dsun.rmi.dgc.server.gcInterval=7200000and observing if GCs happen every two hours instead.
禁用显式 GC,或者通过设置-Dsun.rmi.dgc.server.gcInterval=7200000和观察 GC 是否每两小时发生一次来测试这个假设。
回答by helderdarocha
It depends on your configurations, since HotSpot configures itself differently in different Java environments. For example, in a server with more than 2GB and two processors some JVMs will be configured in '-server' mode instead of the default '-client' mode, which configure the sizes of the memory spaces (generations) differently, and that has an impact as to when garbage collection will occur.
这取决于您的配置,因为 HotSpot 在不同的 Java 环境中以不同的方式配置自己。例如,在具有超过 2GB 和两个处理器的服务器中,一些 JVM 将被配置为“-server”模式而不是默认的“-client”模式,后者以不同的方式配置内存空间(代)的大小,并且具有关于何时发生垃圾收集的影响。
A full GC can occur automatically, but also if you call the garbage collector in your code (ex: using System.gc()). Automatically, it depends on how the minor collections are behaving.
完整 GC 可以自动发生,但也可以在您在代码中调用垃圾收集器时发生(例如: using System.gc())。自动地,这取决于次要集合的行为方式。
There are at least two algorithms being used. If you are using defaults, a copying algorithm is used for minor collections, and a mark-sweep algorithm for major collections.
至少使用了两种算法。如果您使用默认值,则对次要集合使用复制算法,对主要集合使用标记扫描算法。
A copying algorithm consists of copying used memory from one block to another, and then clearing the space containing the blocks with no references to them. The copying algorithm in the JVM uses uses a large area for objects that are created for the first time (called Eden), and two smaller ones (called survivors). Surviving objects are copied once from Edenand several times from the survivorspaces during each minor collection until they become tenured and are copied to another space (called tenuredspace) where they can only be removed in a major collection.
复制算法包括将使用过的内存从一个块复制到另一个块,然后清除包含没有引用它们的块的空间。JVM 中的复制算法对第一次创建的对象(称为Eden)和两个较小的对象(称为survivors)使用较大的区域。幸存的对象在每个次要集合期间Eden从survivor空间复制一次和多次,直到它们成为终身使用并被复制到另一个空间(称为tenured空间),在那里它们只能在主集合中删除。
Most of the objects in Edendie quickly, so the first collection copies the surviving objects to the survivor spaces (which are by default much smaller). There are two survivors s1and s2. Every time the Edenfills, the surviving objects from Edenand s1are copied to s2, Edenand s1are cleared. Next time, survivors from Edenand s2are copied back to s1. They keep on being copied from s1to s2to s1until a certain number of copies is reached, or because a block is too big and doesn't fit, or some other criteria. Then the surviving memory block is copied to the tenuredgeneration.
大多数对象Eden很快消亡,因此第一个集合将幸存的对象复制到幸存空间(默认情况下要小得多)。有两个幸存者s1和s2。每次Eden填充时,来自Eden和的幸存对象s1都被复制到s2,Eden并被s1清除。下一次,来自Eden和 的幸存者s2被复制回s1. 他们不断被复制,从s1到s2到s1直到达到拷贝一定数量,或者因为块太大,不适合,或一些其他标准。然后将幸存的内存块复制到tenured代。
The tenuredobjects are not affected by the minor collections. They accumulate until the area gets full (or the garbage collector is called). Then the JVM will run a mark-sweep algorithm in a major collection which will preserve only the surviving objects that still have references.
该tenured对象不会受到轻微的集合。它们累积直到该区域变满(或调用垃圾收集器)。然后 JVM 将在主要集合中运行标记清除算法,该算法将仅保留仍然具有引用的幸存对象。
If you have larger objects that don't fit into the survivors, they might be copied directly to the tenuredspace, which will fill more quickly and you will get major collections more frequently.
如果您有不适合幸存者的较大对象,它们可能会被直接复制到tenured空间中,这将更快地填充并且您将更频繁地获得主要集合。
Also, the sizes of the survivor spaces, amount of copies between s1and s2, Edensize related to the size of s1and s2, size of the tenured generation, all these may be automatically configured differently in different environments with JVM ergonomics, which may automatically select a -serveror -clientbehavior. You might try to run both JVMs as -serveror -clientand check if they still behave differently.
此外,生存空间的大小,之间拷贝的量s1和s2,Eden有关的尺寸大小s1和s2,年老代的尺寸,所有这些可以被自动地不同的方式在不同的环境中与被配置JVM人体工程学,其可以自动地选择一个-server或-client行为. 您可能会尝试将两个 JVM 都运行为-serveror-client并检查它们的行为是否仍然不同。
回答by Eugene
Even if this will get down votes... My best guess (you will have to test this) would be that the heap needs to expand and when this happens a full gc will be triggered. Not all memory is allocated at once to JVM.
即使这会降低选票......我最好的猜测(你必须测试这个)是堆需要扩展,当发生这种情况时,将触发完整的 gc。并非所有内存都一次性分配给 JVM。
You can test this by setting -Xms and -Xmx to the same value, for example 7GB each
您可以通过将 -Xms 和 -Xmx 设置为相同的值来测试这一点,例如每个 7GB
回答by chris
I have found four conditions that can cause a major GC (given my JVM config):
我发现了四种可能导致主要 GC 的情况(考虑到我的 JVM 配置):
- The old gen area is full (even if it can be grown, a major GC will still be run first)
- The perm gen area is full (even if it can be grown, a major GC will still be run first)
- Someone is manually calling
System.gc(): a bad library or something related to RMI (see links 1, 2and 3) - The young gen areas are all full and nothing is ready to be moved into old gen (see 1)
- old gen 区已满(即使可以增长,仍会先运行一次 Major GC)
- perm gen区已满(即使可以增长,仍会先运行一次major GC)
- 有人手动调用
System.gc():一个坏库或与 RMI 相关的东西(见链接1、2和3) - 年轻代区域都已满,没有准备好移入老一代(参见1)
As others have commented, cases 1 and 2 can be improved by allocating plenty of heap and permgen, and setting -Xmsand -Xmxto the same value (along with the perm equivalents) to avoid dynamic heap resizing.
正如其他人的评论,例1和2可以通过分配大量堆和PermGen的,并设置得到改善-Xms并-Xmx为相同的值(与烫发当量一起),以避免动态堆大小调整。
Case 3 can be avoided using the -XX:+DisableExplicitGCflag.
使用-XX:+DisableExplicitGC标志可以避免情况 3 。
Case 4 requires more involved tuning, e.g., -XX:NewRatio=N(see Oracle's tuning guide).
情况 4 需要更多的调优,例如-XX:NewRatio=N(参见Oracle 的调优指南)。

