最近,我尝试在我的 java 处理器中使用 jdk1.7.0-17 中的 G1GC,该处理器正在处理从 MQ 接收到的许多类似消息(大约 15-20 req/sec)。每条消息都在由 Java 有限线程池服务的单独线程(大约 100 个处于稳定状态的线程)中处理。令人惊讶的是,我检测到了奇怪的行为 - 一旦 GC 开始完整的 gc 循环,它就开始使用大量的处理时间(高达 100% 的 CPU 甚至更多)。我多次重构代码,目标是优化它并使其更轻量级。但没有任何显着的结果 - 行为是相同的。我使用带有 Debian OS(2.6.32-5 内核)的 4 核 64 位机器。有人可以帮助我了解和解决这种情况吗?下面描述了上述问题的一些插图。
1 回答
令人惊讶的是,我检测到了奇怪的行为 - 一旦 GC 开始完整的 gc 循环......
不幸的是,这并不奇怪,因为在 JVM 中实现的 G1 GC 仅使用一个硬件线程 (vCPU) 来执行 Full GC,因此我们的想法是尽量减少 Full GC 的数量。请记住,建议将此收集器用于具有多个内核的配置(当然它不会影响 Full GC,但会影响分配和并行收集)和我认为大于 8GB 的大堆。
根据甲骨文:
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc.html
Garbage-First (G1) 垃圾收集器是一种服务器式垃圾收集器,针对具有大内存的多处理器机器。它试图在实现高吞吐量的同时以高概率满足垃圾收集 (GC) 暂停时间目标。全堆操作,例如全局标记,与应用程序线程同时执行。这可以防止与堆或实时数据大小成比例的中断。
在这篇文章中,有一个关于这个收集器中的 Full GC 单线程的解释。
https://www.redhat.com/en/blog/part-1-introduction-g1-garbage-collector
最后不幸的是,G1 还必须处理可怕的 Full GC。虽然 G1 最终试图避免 Full GC,但它们仍然是一个严酷的现实,尤其是在调整不当的环境中。鉴于 G1 的目标是更大的堆大小,Full GC 的影响可能对进行中的处理和 SLA 造成灾难性影响。主要原因之一是 Full GC 在 G1 中仍然是单线程操作。从原因来看,第一个也是最可以避免的,与元空间有关。
顺便说一句,似乎最新版本的 Java (10) 将包含一个能够并行执行 Full GC 的 G1。
https://www.opsian.com/blog/java-10-with-g1/
Java 10 通过迭代改进其现有算法来减少 Full GC 暂停时间。直到 Java 10 G1 Full GCs 在单线程中运行。没错-您的 32 核服务器和 128GB 将停止并暂停,直到单个线程取出垃圾。
也许,您应该调整元空间或增加堆,或者您可以使用其他收集器,例如并行 GC。