可以增加gc time引用了旧对象的短命对象吗? [英] Could increase gc time short lived object that has references to old lived object?

查看:79
本文介绍了可以增加gc time引用了旧对象的短命对象吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要澄清一下次要gc集合的行为.如果它们在旧空间变大时表现最差

I need some clarification about how minor gc collections behave. calling a() or calling b() in a long-lived application, if they could behave worstly when old space gets bigger

//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
    }

}

我的疑问来自哪里?我发现,在使用的使用权空间变大的应用程序中,gc暂停次数增加了.

Where does my doubt comes from? I found out that in an app in which the used tenured space gets bigger, there is an increase of minor gc pauses.

进行一些测试,我发现如果我强迫jvm使用选项a()和另一个jvm使用选项b(),则当旧空间变大时,带有选项b()的jvm的暂停持续时间较短.但我不知道为什么.

Making some tests I found out that if I force the jvm to use option a() and another jvm to use option b(), then the jvm with option b() has shorter pause duration time when the old space gets bigger but i can't figured out why.

我在4096中使用此属性 XX:ParGCCardsPerStrideChunk 在应用程序中解决了该问题,但我想知道上述情况是否会导致gctimes增大,从而导致gccard表中的扫描速度变慢或我不知道或根本不相关的东西.

I solved that issue in the app, using this property XX:ParGCCardsPerStrideChunk in 4096, but i want to know if situation which i described above can lead in increasing gctimes cause scanning in gccard tables is slower or something that i don't know or is not related at all.

推荐答案

免责声明:到目前为止,我还不是GC专家,但最近为了好玩而深入这些细节.

Disclaimer: I am by far no GC expert, but lately getting into these details for fun.

正如我在评论中所说,您正在使用一个已过时的收集器,没有人支持它,也没人愿意使用它,切换到G1甚至更好的IMHO切换到Shenandoah:从这个简单的开始首先.

As I said in the comments, you are using a collector that is deprecated, no one supports it and no one wants to use it, switch to G1 or even better IMHO switch to Shenandoah : start from this simple thing first.

我只能假定您将其默认值增加了 ParGCCardsPerStrideChunk,并且可能得到了一些ms的帮助(尽管我们没有证明) .我们也没有来自GC,CPU活动,日志等的日志;因此,这很难回答.

I can only assume that you increased ParGCCardsPerStrideChunk from its default value and that probably helped by a few ms (though we have no proof of that). We also have no logs from GC, CPU activity, logs, etc; thus this is pretty complicated to answer.

如果确实有大堆(数十GB)和 年轻空间 ,则您有足够的GC线程,将该参数设置为更大的值可能确实有帮助,可能甚至与您提到的card table有关.进一步阅读原因.

If indeed you have a big heap (tens of GB) and a big young space and you have enough GC Threads, setting that parameter to a bigger value might help indeed and it might even have to do with card table that you are mentioning. Read further why.

CMS将堆分为old spaceyoung space,它可以选择任何其他区分符,但是他们选择了age(就像G1一样).为什么需要那?为了能够仅扫描和收集堆的部分区域(完全扫描它非常昂贵). young space是在stop-the-world暂停的情况下收集的,因此它最好变小,否则您将不满意.这就是为什么为什么您通常会看到与old ones相比更多的young collections.

CMS splits the heap into old space and young space, it could have chosen any other discriminator, but they chose age (just like G1). Why is that needed? To be able to scan and collect only partial regions of the heap (scanning it entirely is very expensive). young space is collected with a stop-the-world pause, so it better be small, otherwise you will not be happy; that is why also why you usually will see many more young collections compare to old ones.

扫描young space时唯一的问题是:如果从old space引用到young space的对象,会发生什么?收集这些显然是错误的,但是扫描整个old space以找出答案将完全破坏generational collections的目的.因此:card table.

The only problem when you scan young space is: what happens if there are references from old space to objects from young space? Collecting those is obviously wrong, but scanning the entire old space to find out that answer would defeat the purpose of generational collections entirely. Thus: card table.

这会跟踪从old spaceyoung space引用的引用,因此它知道什么是垃圾. G1也使用card table,但也添加了RememberedSet(此处不做详细介绍).实际上,RememberedSets实在是太庞大了,这就是G1成为世代相传的原因. (仅供参考:Shenandoah使用matrix而不是card table-使其成为 not 世代的.)

This keeps track of reference from old space to young space references, so it knows what exactly is garbage or not. G1 uses a card table too, but also adds a RememberedSet (not going into the details here). In practice, RememberedSets turned out to be HUGE, that is why G1 became generational. (FYI: Shenandoah uses matrix instead of card table - making it not generational).

因此,这个巨大的介绍旨在表明确实增加了ParGCCardsPerStrideChunk可能有所帮助.您正在为每个GC线程分配更多的空间来进行处理.默认值为256,卡表为512 bytes,表示

So this huge intro, was to show that indeed increasing ParGCCardsPerStrideChunk might have helped. You are giving each GC thread more space to work on. The default value is 256 and card table is 512 bytes, that means

256 * 512 = 128KB per stride of old generation

例如,如果您有一个32 GB堆,那是多少个数十万步幅?可能太多了.

If you for example have a heap of 32 GB how many hundreds of thousands of strides is that? Probably too many.

现在,为什么还要在这里进行讨论?我不知道.

Now, why you also bring reference counting into the discussion here? I have no idea.

您显示的示例具有不同的语义,因此很难推理;不过,我仍然会尝试.您必须了解对象的 reachability 只是从某些(称为GC roots)开始的图.首先让我们看这个例子:

The examples that you have shown have different semantics and as such are kind of difficult to reason about; I'll still try to, though. You have to understand that reachability of Objects is just a graph that starts from some roots (called GC roots). Let's take this example first:

public void b(){
   new ShortLivedObject().doSomething(new Object()); // actually now is shortlived
}

一旦完成doSomething方法调用并且其 scope 仅在该方法之内,

ShortLivedObject实例就会被忘记".因此,其余部分与doSomething:new Object的参数有关.如果doSomething对其获得的参数不做任何操作(通过GC root图使其可访问),则在完成doSomething之后,它也将有资格使用GC.但是,即使doSomething使new Object可以访问,也仍然意味着ShortLivedObject实例有资格使用GC.

ShortLivedObject instance is "forgotten" as soon as doSomething method invocation is done and its scope is within the method only, as such no one can reach it. Thus the remaining part is about the parameter of doSomething : new Object. If doSomething does not do anything fishy with the parameter it got (making it reachable via a GC root graph), then after doSomething is done, it would become eligible for GC too. But even if doSomething makes new Object reachable it still means that ShortLivedObject instance is eligible for GC.

这样,即使Example 可以访问(意味着无法收集),也可能会收集ShortLivedObjectnew Object() .看起来可能像这样:

As such, even if Example is reachable (means it can't be collected), ShortLivedObject and new Object() can potentially be collected. It can look like this:

                 new Object()
                      |
                     \ /
               ShortLivedObject           
                      |
                     \ /
GC Root -> ... - > Example

您会看到,一旦GC将扫描Example实例,它可能根本不会 扫描ShortLivedObject(这就是为什么垃圾被识别为与 live 对象相反).因此,GC算法将简单地丢弃整个图形,而根本不扫描它.

You can see that once GC will scan Example instance, it might not scan ShortLivedObject at all (that is why garbage is identified as the opposite of live objects). So a GC algorithm will simply discard the entire graph and not scan it at all.

第二个示例不同:

public void a(){
    var shortLived = new ShortLivedObject(longLived);
    shortLived.doSomething();
}

区别在于longLived这是一个 instance 字段,因此该图看起来会有所不同:

The difference is that longLived here is an instance field and, as such, the graph will look a bit different:

                ShortLivedObject
                      |
                     \ /
                  longLived         
                     / \
                      |
GC Root -> ... - > Example

很明显,在这种情况下可以收集ShortLivedObject,但是 longLived可以收集.

It's obvious that ShortLivedObject can be collected in this case, but not longLived.

您必须了解,如果可以收集Example实例,那么这根本不重要;该图将不会被遍历,并且可以收集Example使用的所有内容.

What you have to understand that this does not matter at all, if Example instance can be collected; this graph will not be traversed and everything that Example uses can be collected.

您现在应该可以了解,使用方法a 可以保留更多的垃圾,并且可以将其潜在地移动到old space(当它们足够老时)和 can 可能会使您的young pauses更长确实增加了ParGCCardsPerStrideChunk 力量的帮助;但这是高度推测性的,您将需要一种非常糟糕的相同分配模式来完成所有这些工作.没有日志,我高度对此表示怀疑.

You should be able to understand now that using method a can retain a bit more garbage and can potentially move it to old space (when they become old enough) and can potentially make your young pauses be longer and indeed increasing ParGCCardsPerStrideChunk might help a bit; but this is highly speculative and you would need a pretty bad same pattern of allocations to happen for all of this to happen. Without logs, I highly doubt that.

这篇关于可以增加gc time引用了旧对象的短命对象吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆