Java 即使在热点地区没有配备CMS的完整地面军事系统的情况下,老一代的规模如何降低?

Java 即使在热点地区没有配备CMS的完整地面军事系统的情况下,老一代的规模如何降低?,java,garbage-collection,jvm,jvm-hotspot,Java,Garbage Collection,Jvm,Jvm Hotspot,在“Visual GC”选项卡中查看VisualVM时,我注意到,对于使用并发标记和扫描(CMS)收集器运行的应用程序,旧一代的大小可能会减少(以绝对值计算),即使没有启动完整的GC 我以为这不可能?如图所示,即使旧的Gen collection count=4,堆的演变也会有起伏: 我希望看到4次减少(每个完整GC减少一次),或者我想到的唯一理论是,完整GC需要永远执行,而不是一次看到可用内存的巨大下降,而是看到它以小批量缓慢释放。但总的收集时间1分钟18秒似乎很低,这一理论才有意义 以下是

在“Visual GC”选项卡中查看VisualVM时,我注意到,对于使用并发标记和扫描(CMS)收集器运行的应用程序,旧一代的大小可能会减少(以绝对值计算),即使没有启动完整的GC

我以为这不可能?如图所示,即使旧的Gen collection count=4,堆的演变也会有起伏:

我希望看到4次减少(每个完整GC减少一次),或者我想到的唯一理论是,完整GC需要永远执行,而不是一次看到可用内存的巨大下降,而是看到它以小批量缓慢释放。但总的收集时间1分钟18秒似乎很低,这一理论才有意义

以下是我的应用程序设置(在本例中,应用程序是CLion,这些是其默认设置,而不是堆大小):


谢谢

好吧,经过一些挖掘,查看HotSpot的源代码和GC日志,显示这种行为的原因似乎是,显示为“旧代集合”的不是实际的CMS集合(从应用程序一开始就有大量的CMS集合),而是(滑动)Mark&Compact GCs

当JVM得出结论,即下一代年轻的GCs甚至再次运行CMS都不会回收新内存时,将触发(滑动)标记和压缩。在发出OoM错误退出之前,它仍然会尝试停止整个应用程序并运行M&C——因为CMS是一种非压缩算法,所以仍然有可能通过以更压缩的方式重新排列内存中的对象来提供新内存。这将是一个STW操作

我是怎么得出这个结论的?好的,如果您在跟踪级别运行GC日志,您会意识到不时会得到一些
CMS:MSC
(Concurrent Mark&Sweep:Mark Sweep Compact)语句,这些语句似乎与JVisualVM中OldGen中的更新一致。下面是触发该语句的热点代码:

// A work method used by the foreground collector to do
// a mark-sweep-compact.
void CMSCollector::do_compaction_work(bool clear_all_soft_refs) {
  CMSHeap* heap = CMSHeap::heap();

  STWGCTimer* gc_timer = GenMarkSweep::gc_timer();
  gc_timer->register_gc_start();

  SerialOldTracer* gc_tracer = GenMarkSweep::gc_tracer();
  gc_tracer->report_gc_start(heap->gc_cause(), gc_timer->gc_start());

  heap->pre_full_gc_dump(gc_timer);

  GCTraceTime(Trace, gc, phases) t("CMS:MSC");
事实上,通过仔细查看日志,您可以看到这确实是一个标记压缩(我在这里过滤掉信息级别以下的任何内容,因此原始的
CMS:MSC
语句不会显示):

GC(257)完全暂停(分配失败)
GC(257)第1阶段:标记活动对象1966079K(1966080K)
GC(257)元空间:181716K->181716K(1214464K)
GC(257)完全暂停(分配失败)2035M->2033M(2035M)16031.595ms
GC(257)用户=18.01s系统=0.15s实际=16.04s

您的数据显示,主要地面军事系统平均每个近20秒,次要地面军事系统平均不到30毫秒,次要地面军事系统可以与CMS收集器中正在进行的主要地面军事系统交错。考虑到这一点,您关于主要地面军事系统在图形上产生多个特征的假设似乎并不太牵强。当然,小型地面军事系统不会收集终身世代的物体——这就是它们与大型地面军事系统的区别。另一方面,可能存在GC以外的其他活动影响VisualVM对生成大小的测量。我知道,在开始时,在左图中可能会看到OldGen大小下降,这并不罕见——随着分配的发生,堆会增加,因此,使用的OldGen内存量可能会下降(尽管不会下降,ofc)。但是后来,我真的不明白发生了什么。我同意,这可能是其他的神器在起作用。软引用/弱引用/幻影引用与此有关吗?据我所知,引用对象和队列不会改变GC活动的调度。我倾向于推测,活体对象对本地资源的管理更可能导致主要收藏范围之外的终身世代规模显著减少。
// A work method used by the foreground collector to do
// a mark-sweep-compact.
void CMSCollector::do_compaction_work(bool clear_all_soft_refs) {
  CMSHeap* heap = CMSHeap::heap();

  STWGCTimer* gc_timer = GenMarkSweep::gc_timer();
  gc_timer->register_gc_start();

  SerialOldTracer* gc_tracer = GenMarkSweep::gc_tracer();
  gc_tracer->report_gc_start(heap->gc_cause(), gc_timer->gc_start());

  heap->pre_full_gc_dump(gc_timer);

  GCTraceTime(Trace, gc, phases) t("CMS:MSC");
GC(257) Pause Full (Allocation Failure)
GC(257) Phase 1: Mark live objects                       <-- marking
GC(257) Phase 1: Mark live objects 4675.159ms
GC(257) Phase 2: Compute new object addresses
GC(257) Phase 2: Compute new object addresses 1123.827ms
GC(257) Phase 3: Adjust pointers
GC(257) Phase 3: Adjust pointers 2906.019ms
GC(257) Phase 4: Move objects                            <-- compacting
GC(257) Phase 4: Move objects 630.602ms
GC(257) ParNew: 118016K->116364K(118016K)
GC(257) CMS: 1966080K->1966079K(1966080K)
GC(257) Metaspace: 181716K->181716K(1214464K)
GC(257) Pause Full (Allocation Failure) 2035M->2033M(2035M) 16031.595ms
GC(257) User=18.01s Sys=0.15s Real=16.04s