可以增加 gc 时间短生命对象引用旧生命对象吗?
Could increase gc time short lived object that has references to old lived object?
我需要一些关于次要 gc 集合行为的说明。在长期应用程序中调用 a()
或调用 b()
,如果它们在旧的 space 变大时表现最差
//an example instance lives all application life cycle 24x7
public class Example {
private Object longLived = new Object();
public void a(){
var shortLived = new ShortLivedObject(longLived); // longLived now is attribute
shortLived.doSomething();
}
public void b(){
new ShortLivedObject().doSomething(new Object()); // actually now is shortlived
}
}
我的疑惑从何而来?我发现在使用的 tenured space 变大的应用程序中,轻微的 gc 暂停增加。
做一些测试我发现如果我强制 jvm 使用选项 a()
和另一个 jvm 使用选项 b()
,那么带有选项 b()
的 jvm 有更短的停顿旧 space 变大的持续时间,但我不明白为什么。
我在应用程序中解决了这个问题,使用这个 属性 XX:ParGCCardsPerStrideChunk in 4096,但我想知道我上面描述的情况是否会导致增加 gctimes 会导致 gccard 表中的扫描变慢或者我不知道或根本不相关的东西。
免责声明:到目前为止,我不是 GC 专家,但最近为了好玩而了解这些细节。
正如我在评论中所说,您正在使用一个已弃用的收集器,没有人支持它,也没有人想使用它,切换到 G1
或者更好的恕我直言切换到 Shenandoah
:先从这个简单的事情做起。
我只能假设你增加了ParGCCardsPerStrideChunk
的默认值,这可能有一些帮助ms
(尽管我们没有证据证明这一点)。我们也没有来自 GC、CPU activity、日志等的日志;因此这很难回答。
如果你确实有一个大堆(几十GB)and一个大年轻人spaceand你有足够的GC 线程,将该参数设置为更大的值 可能 确实有帮助 它可能 甚至与您提到的 card table
有关.进一步阅读原因。
CMS
将堆拆分为 old space
和 young space
,它本可以选择任何其他鉴别器,但他们选择了 age
(就像 G1
).为什么需要这个?能够仅扫描和收集堆的部分区域(扫描整个区域非常昂贵)。 young space
是用stop-the-world
暂停收集的,所以最好小一点,否则你会不高兴;这就是为什么你通常会看到 young collections
比 old ones
.
更多的原因
扫描 young space
时的唯一问题是:如果 old space
引用了 young space
的对象,会发生什么情况?收集这些显然是错误的,但扫描整个 old space
以找出答案将完全破坏 generational collections
的目的。因此:card table
.
这会跟踪从 old space
到 young space
的引用,因此它知道究竟什么是垃圾。 G1
也用了一个card table
,还加了一个RememberedSet
(这里不赘述)。在实践中,RememberedSets
被证明是巨大的,这就是 G1
成为世代的原因。 (仅供参考:Shenandoah
使用 matrix
而不是 card table
- 使其成为 而非 世代)。
所以这个巨大的介绍,是为了表明确实增加 ParGCCardsPerStrideChunk
可能有所帮助。您正在为每个 GC 线程提供更多 space 的工作。默认值是256
,卡table是512 bytes
,也就是说
256 * 512 = 128KB per stride of old generation
例如,如果你有一堆32 GB
,那是多少数十万的步幅?可能太多了。
现在,为什么您也将 reference counting
带入这里的讨论?我不知道。
您展示的示例具有不同的语义,因此有点难以推理;不过,我仍然会尝试。您必须了解对象的 reachability 只是一个从某些 roots(称为 GC roots
)开始的图形。先举这个例子:
public void b(){
new ShortLivedObject().doSomething(new Object()); // actually now is shortlived
}
ShortLivedObject
实例是 "forgotten" 一旦 doSomething
方法调用完成并且其 scope 仅在方法内,因此没有人能达到它。因此,剩下的部分是关于 doSomething
的参数:new Object
。如果 doSomething
没有对它获得的参数做任何可疑的事情(使其可以通过 GC root
图表访问),那么在 doSomething
完成后,它也将有资格进行 GC。但即使 doSomething
使 new Object
可达,它仍然意味着 ShortLivedObject
实例符合 GC 条件。
因此,即使 Example
是 可达(意味着无法收集),ShortLivedObject
和 new Object()
可以 可能被收集。它可能看起来像这样:
new Object()
|
\ /
ShortLivedObject
|
\ /
GC Root -> ... - > Example
你可以看到,一旦 GC
将扫描 Example
个实例,它可能 不会 扫描 ShortLivedObject
(这就是为什么 garbage 被识别为 live 对象的对立面)。因此 GC 算法将简单地丢弃整个图而不扫描它。
第二个例子不同:
public void a(){
var shortLived = new ShortLivedObject(longLived);
shortLived.doSomething();
}
不同之处在于 longLived
这里是一个 实例 字段,因此,图表看起来会有点不同:
ShortLivedObject
|
\ /
longLived
/ \
|
GC Root -> ... - > Example
显然这种情况下ShortLivedObject
是可以采集的,但是不是longLived
。
什么你要明白这根本不重要,如果Example
实例可以收集;该图不会被遍历,Example
使用的所有内容都可以收集。
您现在应该能够理解,使用方法 a
可以 保留更多的垃圾,并有可能将其移动到 old space
(当他们足够大)并且可以可能使您的young pauses
更长和确实增加ParGCCardsPerStrideChunk
可能 有点帮助;但是这个 是高度推测性的 并且你需要一个非常糟糕的相同分配模式才能发生所有这一切。没有日志,我非常对此表示怀疑。
我需要一些关于次要 gc 集合行为的说明。在长期应用程序中调用 a()
或调用 b()
,如果它们在旧的 space 变大时表现最差
//an example instance lives all application life cycle 24x7
public class Example {
private Object longLived = new Object();
public void a(){
var shortLived = new ShortLivedObject(longLived); // longLived now is attribute
shortLived.doSomething();
}
public void b(){
new ShortLivedObject().doSomething(new Object()); // actually now is shortlived
}
}
我的疑惑从何而来?我发现在使用的 tenured space 变大的应用程序中,轻微的 gc 暂停增加。
做一些测试我发现如果我强制 jvm 使用选项 a()
和另一个 jvm 使用选项 b()
,那么带有选项 b()
的 jvm 有更短的停顿旧 space 变大的持续时间,但我不明白为什么。
我在应用程序中解决了这个问题,使用这个 属性 XX:ParGCCardsPerStrideChunk in 4096,但我想知道我上面描述的情况是否会导致增加 gctimes 会导致 gccard 表中的扫描变慢或者我不知道或根本不相关的东西。
免责声明:到目前为止,我不是 GC 专家,但最近为了好玩而了解这些细节。
正如我在评论中所说,您正在使用一个已弃用的收集器,没有人支持它,也没有人想使用它,切换到 G1
或者更好的恕我直言切换到 Shenandoah
:先从这个简单的事情做起。
我只能假设你增加了ParGCCardsPerStrideChunk
的默认值,这可能有一些帮助ms
(尽管我们没有证据证明这一点)。我们也没有来自 GC、CPU activity、日志等的日志;因此这很难回答。
如果你确实有一个大堆(几十GB)and一个大年轻人spaceand你有足够的GC 线程,将该参数设置为更大的值 可能 确实有帮助 它可能 甚至与您提到的 card table
有关.进一步阅读原因。
CMS
将堆拆分为 old space
和 young space
,它本可以选择任何其他鉴别器,但他们选择了 age
(就像 G1
).为什么需要这个?能够仅扫描和收集堆的部分区域(扫描整个区域非常昂贵)。 young space
是用stop-the-world
暂停收集的,所以最好小一点,否则你会不高兴;这就是为什么你通常会看到 young collections
比 old ones
.
扫描 young space
时的唯一问题是:如果 old space
引用了 young space
的对象,会发生什么情况?收集这些显然是错误的,但扫描整个 old space
以找出答案将完全破坏 generational collections
的目的。因此:card table
.
这会跟踪从 old space
到 young space
的引用,因此它知道究竟什么是垃圾。 G1
也用了一个card table
,还加了一个RememberedSet
(这里不赘述)。在实践中,RememberedSets
被证明是巨大的,这就是 G1
成为世代的原因。 (仅供参考:Shenandoah
使用 matrix
而不是 card table
- 使其成为 而非 世代)。
所以这个巨大的介绍,是为了表明确实增加 ParGCCardsPerStrideChunk
可能有所帮助。您正在为每个 GC 线程提供更多 space 的工作。默认值是256
,卡table是512 bytes
,也就是说
256 * 512 = 128KB per stride of old generation
例如,如果你有一堆32 GB
,那是多少数十万的步幅?可能太多了。
现在,为什么您也将 reference counting
带入这里的讨论?我不知道。
您展示的示例具有不同的语义,因此有点难以推理;不过,我仍然会尝试。您必须了解对象的 reachability 只是一个从某些 roots(称为 GC roots
)开始的图形。先举这个例子:
public void b(){
new ShortLivedObject().doSomething(new Object()); // actually now is shortlived
}
ShortLivedObject
实例是 "forgotten" 一旦 doSomething
方法调用完成并且其 scope 仅在方法内,因此没有人能达到它。因此,剩下的部分是关于 doSomething
的参数:new Object
。如果 doSomething
没有对它获得的参数做任何可疑的事情(使其可以通过 GC root
图表访问),那么在 doSomething
完成后,它也将有资格进行 GC。但即使 doSomething
使 new Object
可达,它仍然意味着 ShortLivedObject
实例符合 GC 条件。
因此,即使 Example
是 可达(意味着无法收集),ShortLivedObject
和 new Object()
可以 可能被收集。它可能看起来像这样:
new Object()
|
\ /
ShortLivedObject
|
\ /
GC Root -> ... - > Example
你可以看到,一旦 GC
将扫描 Example
个实例,它可能 不会 扫描 ShortLivedObject
(这就是为什么 garbage 被识别为 live 对象的对立面)。因此 GC 算法将简单地丢弃整个图而不扫描它。
第二个例子不同:
public void a(){
var shortLived = new ShortLivedObject(longLived);
shortLived.doSomething();
}
不同之处在于 longLived
这里是一个 实例 字段,因此,图表看起来会有点不同:
ShortLivedObject
|
\ /
longLived
/ \
|
GC Root -> ... - > Example
显然这种情况下ShortLivedObject
是可以采集的,但是不是longLived
。
什么你要明白这根本不重要,如果Example
实例可以收集;该图不会被遍历,Example
使用的所有内容都可以收集。
您现在应该能够理解,使用方法 a
可以 保留更多的垃圾,并有可能将其移动到 old space
(当他们足够大)并且可以可能使您的young pauses
更长和确实增加ParGCCardsPerStrideChunk
可能 有点帮助;但是这个 是高度推测性的 并且你需要一个非常糟糕的相同分配模式才能发生所有这一切。没有日志,我非常对此表示怀疑。