为什么 运行 单线程 Java 程序会导致许多内核处于活动状态?
Why does running a single threaded Java program result in many cores active?
我正在对从 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++);
}
当 运行 上述代码 CPU 在我的 Intel i5 8250u(4 核)上的利用率 CPU 利用率为 100% 运行 在 IntelliJ Idea 上。所以我认为这可能是因为 IntelliJ,因此我将代码移至具有 20 个内核的 Azure VM (运行 CentOS 7.4),令我惊讶的是,这段代码最终消耗了 1500% CPU(来自 top
命令的结果)是 15 个核心。
我无法理解的是:单线程Java程序代码如何消耗超过1个内核?
EDIT:
重现步骤:
运行上面的代码。
机器配置:
笔记本电脑:4 核 16Gb 内存,Oracle Java 1.8_161
Azure VM:20 核 148GB RAM,Oracle Java 1.8_161
笔记本电脑上 JVisualVM 的输出:
您的测试只分配内存。所以它很快就耗尽了初始堆内存,导致 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]
...
因此,您观察到的是一系列长的 Full GC 周期。 JDK8默认的垃圾收集器是Parallel,并行GC线程数等于CPU个数。
如果你运行async-profiler处于线程模式(-t
),你会发现几乎所有CPU时间都花在了运行垃圾上多线程收集。
我正在对从 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++);
}
当 运行 上述代码 CPU 在我的 Intel i5 8250u(4 核)上的利用率 CPU 利用率为 100% 运行 在 IntelliJ Idea 上。所以我认为这可能是因为 IntelliJ,因此我将代码移至具有 20 个内核的 Azure VM (运行 CentOS 7.4),令我惊讶的是,这段代码最终消耗了 1500% CPU(来自 top
命令的结果)是 15 个核心。
我无法理解的是:单线程Java程序代码如何消耗超过1个内核?
EDIT:
重现步骤:
运行上面的代码。
机器配置:
笔记本电脑:4 核 16Gb 内存,Oracle Java 1.8_161
Azure VM:20 核 148GB RAM,Oracle Java 1.8_161
笔记本电脑上 JVisualVM 的输出:
您的测试只分配内存。所以它很快就耗尽了初始堆内存,导致 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]
...
因此,您观察到的是一系列长的 Full GC 周期。 JDK8默认的垃圾收集器是Parallel,并行GC线程数等于CPU个数。
如果你运行async-profiler处于线程模式(-t
),你会发现几乎所有CPU时间都花在了运行垃圾上多线程收集。