为什么运行单线程 Java 程序会导致许多内核处于活动状态?

Sai*_*mad 5 java performance benchmarking multithreading jvm

我正在对从Object将具有的数据类型转换的总延迟进行基准测试。但是我遇到了一个非常奇怪的 Java 集合行为,在这种情况下,一个List.

List<Long> data = new ArrayList<>();
int SIZE = 50_000_000;

long currentTime = System.currentTimeMillis();
for (int i = 0; i < SIZE; i++) {
    data.add(currentTime++);
}
Run Code Online (Sandbox Code Playgroud)

在我的Intel i5 8250u(4 核)上运行上述代码时,CPU 利用率在 IntelliJ Idea 上为 100%。所以我认为这可能是因为 IntelliJ,因此我将代码移动到具有 20 个内核的 Azure VM(运行 CentOS 7.4),令我惊讶的是,这段代码最终消耗了1500% 的CPU(来自top命令的结果),即 15 个内核。

我无法理解的是:单线程 Java 程序代码如何消耗 1 个以上的内核?

编辑:

重现步骤:

运行上面的代码。

机器配置:

笔记本电脑:4 核 16Gb RAM,Oracle Java 1.8_161

Azure VM:20 核 148GB RAM,Oracle Java 1.8_161

笔记本电脑上 JVisualVM 的输出:

在此处输入图片说明

在此处输入图片说明

apa*_*gin 6

您的测试只会分配内存。所以它很快耗尽了初始堆内存,导致 Full GC 运行。然后堆增加,但它又很快被填满,导致另一个 Full GC,等等。

$ java -XX:+PrintGC Test
[GC (Allocation Failure)  27648K->20757K(104448K), 0.0296779 secs]
[GC (Allocation Failure)  48405K->40538K(132096K), 0.0293287 secs]
[GC (Allocation Failure)  83084K->82453K(138752K), 0.0615143 secs]
[Full GC (Ergonomics)  82453K->75113K(225792K), 0.5392036 secs]
[GC (Allocation Failure)  124981K->139346K(254464K), 0.0563272 secs]
[Full GC (Ergonomics)  139346K->112504K(353792K), 0.5240216 secs]
[GC (Allocation Failure)  185709K->208841K(380416K), 0.0864858 secs]
[Full GC (Ergonomics)  208841K->168513K(512512K), 0.9035611 secs]
...
Run Code Online (Sandbox Code Playgroud)

因此,您观察到的是一系列长的 Full GC 周期。JDK 8 中默认的垃圾收集器是 Parallel,并行 GC 线程的数量等于 CPU 的数量。

如果您以线程模式 ( )运行async-profiler-t,您会发现几乎所有 CPU 时间都用于在多个线程中运行垃圾收集。

CPU配置文件