我用默认的垃圾收集器(g1gc)运行一个java应用程序。我不知道g1cc什么时候释放内存。在 htop 我看到了 700M 应用程序使用的位置。在我做了之后 jcmd <pid> GC.run 下降到大约 250M . 这是不是意味着gc不会自己做这些?
htop
700M
jcmd <pid> GC.run
250M
ijxebb2r1#
总结在jdk 11之前,堆收缩(将内存返回操作系统)只在完全收集之后触发,因为更改的重要性足以保证该操作不会影响性能。使用jdk12+(检查到15),堆收缩也会在正常的收集周期中触发,这使得通常不涉及完整收集的应用程序更有可能看到堆收缩。细节g1 gc仅在空间耗尽的情况下执行完全收集,除非手动触发(如您所做的那样):[…]如果应用程序在收集活动性信息时内存不足,g1会像其他收集器一样执行就地停止世界完全堆压缩(full gc)[…]收集周期由仅年轻阶段和空间回收阶段组成。如果最初分配新对象的关联eden空间耗尽,则会发生“仅年轻”阶段。可触及的物体被疏散(移动)到幸存者空间。如果幸存者空间耗尽,可到达的对象将被疏散到另一个幸存者空间或旧一代(如果对象可到达或“存活”给定数量的集合)。空间回收取决于旧代的大小,由初始堆占用百分比(ihop)控制。默认值( -XX:InitiatingHeapOccupancyPercent )是45%,但ihop由gc调整以优化收集间隔(称为“自适应ihop”)。这个阶段也处理年轻一代,因此它被称为“混合集合”。g1 gc旨在[…]在延迟和吞吐量之间提供最佳平衡[…]g1尝试将垃圾收集延迟尽可能长的时间,并尝试避免完全的垃圾收集。有关详细信息,请参阅https://docs.oracle.com/javase/9/gctuning/garbage-first-garbage-collector.htm#jsgct-guid-1cdeb6b6-9463-4998-815d-05e095bfbd0fgc试图避免堆空间的释放(将内存还给操作系统),因为如果在以后某个时间点需要这些空间,那么这可能会导致性能下降。根据jdk9中的g1源代码,收缩只在完全收集(显式收集或不显式收集)之后进行,并且只在满足特定阈值的情况下进行。 G1CollectedHeap::do_full_collection 电话 G1CollectedHeap::resize_if_necessary_after_full_collection ,它处理收缩过程并生成以下gc日志输出:
-XX:InitiatingHeapOccupancyPercent
G1CollectedHeap::do_full_collection
G1CollectedHeap::resize_if_necessary_after_full_collection
log_debug(gc, ergo, heap)( "Shrink the heap. requested shrinking amount: " SIZE_FORMAT "B aligned shrinking amount: " SIZE_FORMAT "B attempted shrinking amount: " SIZE_FORMAT "B", shrink_bytes, aligned_shrink_bytes, shrunk_bytes);
根据jdk15中的g1源代码,shinking是针对完整集合以及并发的mark和sweep集合进行的。 G1FullCollector::complete_collection 电话 G1CollectedHeap::prepare_heap_for_mutators 反过来又叫 G1CollectedHeap::resize_heap_if_necessary . 实际的收缩过程和日志消息与jdk9相同。与jdk9相比,jdk12及更高版本在所谓的重新标记阶段也执行收缩( G1ConcurrentMark::remark 电话 G1CollectedHeap::resize_heap_if_necessary ),在正常收集周期的上下文中。这一变化是由以下因素引起的:http://hg.openjdk.java.net/jdk/jdk/rev/08041b0d7c08一种简单的监控方法是 -verbose:gc . 使用 -Xlog:gc=debug 在收缩的情况下查看上面提到的日志输出。
G1FullCollector::complete_collection
G1CollectedHeap::prepare_heap_for_mutators
G1CollectedHeap::resize_heap_if_necessary
G1ConcurrentMark::remark
-verbose:gc
-Xlog:gc=debug
1条答案
按热度按时间ijxebb2r1#
总结
在jdk 11之前,堆收缩(将内存返回操作系统)只在完全收集之后触发,因为更改的重要性足以保证该操作不会影响性能。
使用jdk12+(检查到15),堆收缩也会在正常的收集周期中触发,这使得通常不涉及完整收集的应用程序更有可能看到堆收缩。
细节
g1 gc仅在空间耗尽的情况下执行完全收集,除非手动触发(如您所做的那样):
[…]如果应用程序在收集活动性信息时内存不足,g1会像其他收集器一样执行就地停止世界完全堆压缩(full gc)[…]
收集周期由仅年轻阶段和空间回收阶段组成。
如果最初分配新对象的关联eden空间耗尽,则会发生“仅年轻”阶段。可触及的物体被疏散(移动)到幸存者空间。如果幸存者空间耗尽,可到达的对象将被疏散到另一个幸存者空间或旧一代(如果对象可到达或“存活”给定数量的集合)。
空间回收取决于旧代的大小,由初始堆占用百分比(ihop)控制。默认值(
-XX:InitiatingHeapOccupancyPercent
)是45%,但ihop由gc调整以优化收集间隔(称为“自适应ihop”)。这个阶段也处理年轻一代,因此它被称为“混合集合”。g1 gc旨在
[…]在延迟和吞吐量之间提供最佳平衡[…]
g1尝试将垃圾收集延迟尽可能长的时间,并尝试避免完全的垃圾收集。
有关详细信息,请参阅https://docs.oracle.com/javase/9/gctuning/garbage-first-garbage-collector.htm#jsgct-guid-1cdeb6b6-9463-4998-815d-05e095bfbd0f
gc试图避免堆空间的释放(将内存还给操作系统),因为如果在以后某个时间点需要这些空间,那么这可能会导致性能下降。
根据jdk9中的g1源代码,收缩只在完全收集(显式收集或不显式收集)之后进行,并且只在满足特定阈值的情况下进行。
G1CollectedHeap::do_full_collection
电话G1CollectedHeap::resize_if_necessary_after_full_collection
,它处理收缩过程并生成以下gc日志输出:根据jdk15中的g1源代码,shinking是针对完整集合以及并发的mark和sweep集合进行的。
G1FullCollector::complete_collection
电话G1CollectedHeap::prepare_heap_for_mutators
反过来又叫G1CollectedHeap::resize_heap_if_necessary
. 实际的收缩过程和日志消息与jdk9相同。与jdk9相比,jdk12及更高版本在所谓的重新标记阶段也执行收缩(G1ConcurrentMark::remark
电话G1CollectedHeap::resize_heap_if_necessary
),在正常收集周期的上下文中。这一变化是由以下因素引起的:
http://hg.openjdk.java.net/jdk/jdk/rev/08041b0d7c08
一种简单的监控方法是
-verbose:gc
. 使用-Xlog:gc=debug
在收缩的情况下查看上面提到的日志输出。