5

我已经使用单线程 Java 代码实现了一个算法。当我使用启用 JIT 编译运行我的程序时,它会使我机器上的所有 8 个内核饱和。当我使用-XintJVM 选项运行相同的程序以禁用 JIT 编译时,它按预期在单个内核上运行。

这是我的 Java 版本信息:

java version "1.7.0_25"
OpenJDK Runtime Environment (IcedTea 2.3.10) (7u25-2.3.10-1ubuntu0.12.10.2)
OpenJDK 64-Bit Server VM (build 23.7-b01, mixed mode)

为什么我的代码似乎被并行化了,我在哪里可以找到有关 HotSpot 何时可以并行化代码的更多信息?

4

3 回答 3

7

Java 不会自动并行化代码,我猜你看到的核心饱和是 JIT 编译你的代码。使您的测试程序输入更大,使其运行时间更长(可能是 2-3 分钟),看看它是否会在一段时间后停止。

于 2013-10-03T16:31:20.690 回答
2

它不会直接自动并行化您的代码,但它会使用更多的机器资源来使代码运行得更快。它基于运行时数据进行分析、编译、垃圾收集和不断重新编译。只要有可能,这些操作将在其他 CPU 上完成。

它可能会决定您使用相同的参数调用足够多的方法,以便完全内联该参数的结果,或者如果给定方法中的许多 if 语句从未被采用,它可能会优化它们,如果您有不同的结果,则会导致原始参数。它不希望这些操作减慢/阻塞您的程序,因此它在不同的线程上执行它们。

我猜如果你跑得足够长,虽然你会看到它回到填充单个 cpu 的状态。

于 2013-10-03T16:36:18.487 回答
1

上面给出的答案大体上是正确的。我只是回答完成图片。显然 JVM 不会自动并行化用户代码,它有自己的线程在运行。这些线程的数量各不相同,但通常我们有以下线程。此线程堆栈快照是在热点 (OpenJDK) JVM 的实时阶段开始时拍摄的。我们可以看到这里有 11 个线程,可以轻松占据八核或四核机器的所有核心。线程名称也解释了它们的用途。

Full thread dump OpenJDK 64-Bit Server VM (25.71-b00-debug mixed mode):

"Service Thread" #9 daemon prio=9 os_prio=0 tid=0x00007ffff0160800 nid=0x2f91 runnable [0x0000000000000000]

"C1 CompilerThread3" #8 daemon prio=9 os_prio=0 tid=0x00007ffff0158800 nid=0x2f90 waiting on condition [0x0000000000000000]

"C2 CompilerThread2" #7 daemon prio=9 os_prio=0 tid=0x00007ffff0156000 nid=0x2f8f waiting on condition [0x0000000000000000]

"C2 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007ffff0153000 nid=0x2f8e waiting on condition [0x0000000000000000]

"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007ffff0150800 nid=0x2f8d waiting on condition [0x0000000000000000]

"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007ffff014e800 nid=0x2f8c runnable [0x0000000000000000]

"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007ffff0103800 nid=0x2f8b in Object.wait() [0x00007ffff412f000]

"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007ffff00fa000 nid=0x2f8a in Object.wait() [0x00007ffff4230000]

"main" #1 prio=5 os_prio=0 tid=0x00007ffff000d000 nid=0x2f86 runnable [0x00007ffff7fca000]

"VM Thread" os_prio=0 tid=0x00007ffff00ef800 nid=0x2f89 runnable 

"VM Periodic Task Thread" os_prio=0 tid=0x00007ffff015f000 nid=0x2f92 waiting on condition 

同样,下面的线程堆栈快照显示了垃圾收集器的运行情况。这张快照是在 GC 调用之后拍摄的。GC 单独有 8 个线程,因为我正在运行 GC 的并行实现(我猜 GC 线程等于内核数,因为我正在八核机器上进行测试)。

Full thread dump OpenJDK 64-Bit Server VM (25.71-b00-debug mixed mode):

"Service Thread" #9 daemon prio=9 os_prio=0 tid=0x00007ffff017e800 nid=0xaa1 runnable  (no locks) [0x0000000000000000]

"C1 CompilerThread3" #8 daemon prio=9 os_prio=0 tid=0x00007ffff016e800 nid=0xaa0 waiting on condition  (no locks) [0x0000000000000000]

"C2 CompilerThread2" #7 daemon prio=9 os_prio=0 tid=0x00007ffff016b800 nid=0xa9f waiting on condition  (no locks) [0x0000000000000000]

"C2 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007ffff0169800 nid=0xa9e waiting on condition  (no locks) [0x0000000000000000]

"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007ffff0166000 nid=0xa9d waiting on condition  (no locks) [0x0000000000000000]

"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007ffff0164800 nid=0xa9c runnable  (no locks) [0x0000000000000000]

"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007ffff0119000 nid=0xa9b in Object.wait()  (no locks) [0x00007fffba33d000]

"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007ffff0110000 nid=0xa9a in Object.wait()  (no locks) [0x00007fffba43e000]

"main" #1 prio=5 os_prio=0 tid=0x00007ffff000d000 nid=0xa8b runnable  (no locks) [0x00007ffff7fc9000]

"VM Thread" os_prio=0 tid=0x00007ffff0105000 nid=0xa99 runnable  (no locks) 

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007ffff0026800 nid=0xa91 runnable  (no locks) 

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007ffff0028800 nid=0xa92 runnable  (no locks) 

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00007ffff002a800 nid=0xa93 runnable  (no locks) 

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00007ffff002c800 nid=0xa94 runnable  (no locks) 

"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x00007ffff002e800 nid=0xa95 runnable  (no locks) 

"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x00007ffff0030800 nid=0xa96 runnable  (no locks) 

"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x00007ffff0032800 nid=0xa97 runnable  (no locks) 

"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x00007ffff0034800 nid=0xa98 runnable  (no locks) 

"VM Periodic Task Thread" os_prio=0 tid=0x00007ffff017f800 nid=0xaa2 waiting on condition  (no locks) 

结论

由于线程数量在运行时会发生变化,并且许多线程会在运行中产生和终止,因此无论运行多长时间,您几乎都不会看到 Java 程序正在使用单个内核。

上面的输出是使用 GDB 和 openJDK 的内部调试 API 生成的。如果有人有兴趣进一步了解这些线程及其用途,可以参考:openJDK 中的线程管理

于 2017-01-12T08:02:18.153 回答