java代码执行在没有断点和正常运行的调试中产生不同的结果。 ExecutorService坏了吗?

TL:DR ExecutorService executorService = Executors.newFixedThreadPool(8); 在debug中运行并发,但在正常运行时它启动并发,但后来在单线程中运行。

我有一些代码,我在ExecutorService启动了4个不同的任务。 其中两个任务应该几乎立即完成,另外两个应该运行一段时间。

这些任务在Future以秒为单位返回执行时间。

此代码负责任务执行和测量:

 public Future measure(int[] arr, ProcessIntArray processIntArray, ExecutorService es) { Callable task = () -> { long start = System.nanoTime(); processIntArray.process(arr); long end = System.nanoTime(); return (end - start) / 1000000000.0; }; return es.submit(task); } 

稍后,在启动这些任务后,我将按照先前运行的执行时间顺序打印相同的输入大小。

  Future bubbleSortTime = measure(bubbleSortArray, Solution::bubbleSort, executorService); Future insertionSortTime = measure(insertionSortArray, Solution::insertionSort, executorService); Future quickSortTime = measure(quickSortArray, Solution::quickSort, executorService); Future mergeSortTime = measure(mergeSortArray, Solution::mergeSort, executorService); System.out.println(); System.out.println("array size: " + size); System.out.println("quick sort: " + quickSortTime.get() + "s"); System.out.println("merge sort: " + mergeSortTime.get() + "s"); System.out.println("insertion sort: " + insertionSortTime.get() + "s"); System.out.println("bubble sort: " + bubbleSortTime.get() + "s"); 

当我在调试模式下运行代码时,会立即打印2个结果,我必须等待第3个结果(我不打扰等待第4个结果)。

在调试开始后(正确和预期):

array size: 1000000 quick sort: 0.186892839s merge sort: 0.291950604s insertion sort: 344.534256723s

正常执行不同,它看起来像方法测量long start = System.nanoTime(); 执行,然后线程进入睡眠状态,并在insertSort完成后,quickSort重新执行,产生输出:

array size: 1000000 quick sort: 345.893922141s merge sort: 345.944023095s insertion sort: 345.871908569s

这是错的。 所有这些线程应该同时运行,如newFixedThreadPool javadoc。

 /** * Creates a thread pool that reuses a fixed number of threads * operating off a shared unbounded queue. At any point, at most * {@code nThreads} threads will be active processing tasks. * If additional tasks are submitted when all threads are active, * they will wait in the queue until a thread is available. * If any thread terminates due to a failure during execution * prior to shutdown, a new one will take its place if needed to * execute subsequent tasks. The threads in the pool will exist * until it is explicitly {@link ExecutorService#shutdown shutdown}. * * @param nThreads the number of threads in the pool * @return the newly created thread pool * @throws IllegalArgumentException if {@code nThreads <= 0} */ public static ExecutorService newFixedThreadPool(int nThreads) 

我附上源代码:

 import java.util.Arrays; import java.util.Random; import java.util.concurrent.*; class ThreadedSortingComparsion { Random random = new Random(System.currentTimeMillis()); void popul(int[] array) { for (int i = 0; i < array.length; i++) { array[i] = random.nextInt(); } } interface ArraySorter { void sort(int[] array); } public Future measureTime(int[] array, ArraySorter arraySorter, ExecutorService executorService) { Callable task = () -> { long start = System.nanoTime(); arraySorter.sort(array); long end = System.nanoTime(); return (end - start) / 1000000000.0; }; return executorService.submit(task); } public void start() throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(8); int size = 1000 * 1000; int[] quickSortArray = new int[size]; popul(quickSortArray); int[] bubbleSortArray = Arrays.copyOf(quickSortArray, quickSortArray.length); int[] mergeSortArray = Arrays.copyOf(quickSortArray, quickSortArray.length); int[] originalArray = Arrays.copyOf(quickSortArray, quickSortArray.length); int[] insertionSortArray = Arrays.copyOf(quickSortArray, quickSortArray.length); Future bubbleSortTime = measureTime(bubbleSortArray, ThreadedSortingComparsion::bubbleSort, executorService); Future insertionSortTime = measureTime(insertionSortArray, ThreadedSortingComparsion::insertionSort, executorService); Future quickSortTime = measureTime(quickSortArray, ThreadedSortingComparsion::quickSort, executorService); Future mergeSortTime = measureTime(mergeSortArray, ThreadedSortingComparsion::mergeSort, executorService); System.out.println(); System.out.println("array size: " + size); System.out.println("quick sort: " + quickSortTime.get() + "s"); System.out.println("merge sort: " + mergeSortTime.get() + "s"); System.out.println("insertion sort: " + insertionSortTime.get() + "s"); System.out.println("bubble sort: " + bubbleSortTime.get() + "s"); executorService.shutdown(); for (int i = 0; i < quickSortArray.length; i++) { if (quickSortArray[i] != bubbleSortArray[i] || quickSortArray[i] != mergeSortArray[i] || quickSortArray[i] != insertionSortArray[i]) { throw new RuntimeException(Arrays.toString(originalArray)); } } } public static void mergeSort(int[] ar) { if (ar.length < 5) { bubbleSort(ar); return; } int middle = ar.length / 2; int[] arrayLeft = new int[middle]; int[] arrayRight = new int[ar.length - middle]; for (int i = 0; i < ar.length; i++) { if (i < middle) { arrayLeft[i] = ar[i]; } else { arrayRight[i - middle] = ar[i]; } } mergeSort(arrayLeft); mergeSort(arrayRight); int indexLeft = 0; int indexRight = 0; int inputArrayIndex = 0; while (true) { int whatToPutInAR = 0; if (indexLeft != arrayLeft.length && indexRight != arrayRight.length) { if (arrayLeft[indexLeft] < arrayRight[indexRight]) { whatToPutInAR = arrayLeft[indexLeft]; indexLeft++; } else { whatToPutInAR = arrayRight[indexRight]; indexRight++; } } else if (indexLeft != arrayLeft.length) { whatToPutInAR = arrayLeft[indexLeft]; indexLeft++; } else if (indexRight != arrayRight.length) { whatToPutInAR = arrayRight[indexRight]; indexRight++; } if (inputArrayIndex == ar.length) return; ar[inputArrayIndex++] = whatToPutInAR; } } private static void quickSort(int[] ar) { quickSort(ar, 0, ar.length); } static public void quickSort(int[] array, int start, int end) { boolean changed = false; if (end == 0) return; int pivot = array[end - 1]; int partitionCandidate = start; for (int i = start; i < end; i++) { if (array[i] < pivot) { swap(array, partitionCandidate++, i); changed = true; } else if (pivot < array[i]) { swap(array, end - 1, i); changed = true; } } if (start < partitionCandidate) { quickSort(array, start, partitionCandidate); } if (partitionCandidate < end) { if (partitionCandidate != start || changed) quickSort(array, partitionCandidate, end); } } public static void swap(int[] ar, int from, int to) { int old = ar[from]; ar[from] = ar[to]; ar[to] = old; } public static void bubbleSort(int[] array) { for (int i = 0; i < array.length; i++) { for (int j = 0; j  array[j + 1]) { swap(array, j + 1, j); } } } } private static void insertionSort(int[] ar) { for (int i = 0; i = 1; j--) { boolean breaker = true; if (ar[j] < ar[j - 1]) { breaker = false; swap(ar, j - 1, j); } if (breaker) break; } } } public static void main(String[] args) throws ExecutionException, InterruptedException { ThreadedSortingComparsion s = new ThreadedSortingComparsion(); s.start(); } } 

编辑:当我在Ideone中运行它时,代码工作正常。 http://ideone.com/1E8C51 Ideone有java版本1.8.0_51我在1.8.0_91上测试过。 并且1.8.0_45 。 为什么它适用于ideone,而不是我测试过的另外两台PC?

当我进行线程转储,而不是在调试中执行时,我等待一段时间,并在打印线程转储后,也打印结果。 因此,在完成插入排序后完成线程转储。

 "C:\Program Files\Java\jdk1.8.0_45\bin\java" -Xmx8G -Xss1G -Didea.launcher.port=7533 "-Didea.launcher.bin.path=C:\Program Files (x86)\JetBrains\IntelliJ IDEA Community Edition 2016.1.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_45\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_45\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_45\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_45\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_45\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_45\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_45\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_45\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_45\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_45\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_45\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_45\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_45\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_45\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_45\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_45\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_45\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_45\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_45\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_45\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_45\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_45\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_45\jre\lib\rt.jar;C:\Users\Tomasz_Mielczarski\IdeaProjects\untitled\out\production\untitled;C:\Program Files (x86)\JetBrains\IntelliJ IDEA Community Edition 2016.1.3\lib\idea_rt.jar" com.intellij.rt.execution.application.AppMain ThreadedSortingComparsion array size: 1000000 2016-07-15 13:45:22 Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.45-b02 mixed mode): "pool-1-thread-4" #15 prio=5 os_prio=0 tid=0x00000000696bd000 nid=0x560 runnable [0x00000002fffee000] java.lang.Thread.State: RUNNABLE at ThreadedSortingComparsion.mergeSort(ThreadedSortingComparsion.java:77) at ThreadedSortingComparsion.mergeSort(ThreadedSortingComparsion.java:78) at ThreadedSortingComparsion.mergeSort(ThreadedSortingComparsion.java:78) at ThreadedSortingComparsion.mergeSort(ThreadedSortingComparsion.java:78) at ThreadedSortingComparsion.mergeSort(ThreadedSortingComparsion.java:77) at ThreadedSortingComparsion$$Lambda$5/81628611.sort(Unknown Source) at ThreadedSortingComparsion.lambda$measureTime$0(ThreadedSortingComparsion.java:21) at ThreadedSortingComparsion$$Lambda$2/1023892928.call(Unknown Source) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) "pool-1-thread-3" #14 prio=5 os_prio=0 tid=0x00000000696bb800 nid=0x2634 runnable [0x00000002bffee000] java.lang.Thread.State: RUNNABLE at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:123) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:126) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:123) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:126) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:126) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:123) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:123) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:123) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:123) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:123) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:123) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:123) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:123) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:123) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:123) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:126) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:123) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:123) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:126) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:123) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:126) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:126) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:123) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:126) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:126) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:123) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:126) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:126) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:126) at ThreadedSortingComparsion.quickSort(ThreadedSortingComparsion.java:105) at ThreadedSortingComparsion$$Lambda$4/1989780873.sort(Unknown Source) at ThreadedSortingComparsion.lambda$measureTime$0(ThreadedSortingComparsion.java:21) at ThreadedSortingComparsion$$Lambda$2/1023892928.call(Unknown Source) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) "pool-1-thread-2" #13 prio=5 os_prio=0 tid=0x00000000696b7800 nid=0x1c70 waiting on condition [0x000000027ffef000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for  (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1067) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) "pool-1-thread-1" #12 prio=5 os_prio=0 tid=0x00000000696b6800 nid=0x478 runnable [0x000000023ffee000] java.lang.Thread.State: RUNNABLE at ThreadedSortingComparsion.bubbleSort(ThreadedSortingComparsion.java:139) at ThreadedSortingComparsion$$Lambda$1/990368553.sort(Unknown Source) at ThreadedSortingComparsion.lambda$measureTime$0(ThreadedSortingComparsion.java:21) at ThreadedSortingComparsion$$Lambda$2/1023892928.call(Unknown Source) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) "Monitor Ctrl-Break" #11 daemon prio=5 os_prio=0 tid=0x0000000068d3d000 nid=0x2f3c runnable [0x00000001fffee000] java.lang.Thread.State: RUNNABLE at java.net.SocketInputStream.socketRead0(Native Method) at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) at java.net.SocketInputStream.read(SocketInputStream.java:170) at java.net.SocketInputStream.read(SocketInputStream.java:141) at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284) at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326) at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178) - locked  (a java.io.InputStreamReader) at java.io.InputStreamReader.read(InputStreamReader.java:184) at java.io.BufferedReader.fill(BufferedReader.java:161) at java.io.BufferedReader.readLine(BufferedReader.java:324) - locked  (a java.io.InputStreamReader) at java.io.BufferedReader.readLine(BufferedReader.java:389) at com.intellij.rt.execution.application.AppMain$1.run(AppMain.java:93) at java.lang.Thread.run(Thread.java:745) "Service Thread" #10 daemon prio=9 os_prio=0 tid=0x0000000068c81000 nid=0x2d6c runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C1 CompilerThread3" #9 daemon prio=9 os_prio=2 tid=0x0000000068bea800 nid=0x1ad0 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread2" #8 daemon prio=9 os_prio=2 tid=0x0000000068be4000 nid=0x17d0 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread1" #7 daemon prio=9 os_prio=2 tid=0x0000000068bdd800 nid=0x3238 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread0" #6 daemon prio=9 os_prio=2 tid=0x0000000068bda000 nid=0x1824 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x0000000068bd8800 nid=0x910 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x0000000068bd7800 nid=0x31f8 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Finalizer" #3 daemon prio=8 os_prio=1 tid=0x0000000043229800 nid=0x2810 in Object.wait() [0x00000000fffee000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on  (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143) - locked  (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209) "Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000000043223000 nid=0xd48 in Object.wait() [0x00000000bffef000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on  (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Object.java:502) at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:157) - locked  (a java.lang.ref.Reference$Lock) "main" #1 prio=5 os_prio=0 tid=0x000000000311d800 nid=0x2ed0 waiting on condition [0x000000004311e000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for  (a java.util.concurrent.FutureTask) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.FutureTask.awaitDone(FutureTask.java:429) at java.util.concurrent.FutureTask.get(FutureTask.java:191) at ThreadedSortingComparsion.start(ThreadedSortingComparsion.java:48) at ThreadedSortingComparsion.main(ThreadedSortingComparsion.java:162) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144) "VM Thread" os_prio=2 tid=0x0000000056348800 nid=0x2984 runnable "GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000043147000 nid=0x27e0 runnable "GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000043148800 nid=0x20b4 runnable "GC task thread#2 (ParallelGC)" os_prio=0 tid=0x000000004314a000 nid=0x1da4 runnable "GC task thread#3 (ParallelGC)" os_prio=0 tid=0x000000004314c000 nid=0x29e0 runnable "GC task thread#4 (ParallelGC)" os_prio=0 tid=0x000000004314e000 nid=0xa04 runnable "GC task thread#5 (ParallelGC)" os_prio=0 tid=0x0000000043150000 nid=0x14b8 runnable "GC task thread#6 (ParallelGC)" os_prio=0 tid=0x0000000043153800 nid=0xf00 runnable "GC task thread#7 (ParallelGC)" os_prio=0 tid=0x0000000043154800 nid=0x243c runnable "VM Periodic Task Thread" os_prio=2 tid=0x0000000068c82000 nid=0x22d8 waiting on condition JNI global references: 224 Heap PSYoungGen total 76288K, used 13755K [0x0000000715580000, 0x000000071aa80000, 0x00000007c0000000) eden space 65536K, 4% used [0x0000000715580000,0x0000000715874910,0x0000000719580000) from space 10752K, 99% used [0x0000000719580000,0x0000000719ffa348,0x000000071a000000) to space 10752K, 0% used [0x000000071a000000,0x000000071a000000,0x000000071aa80000) ParOldGen total 175104K, used 33211K [0x00000005c0000000, 0x00000005cab00000, 0x0000000715580000) object space 175104K, 18% used [0x00000005c0000000,0x00000005c206ed30,0x00000005cab00000) Metaspace used 4277K, capacity 4790K, committed 4992K, reserved 1056768K class space used 484K, capacity 535K, committed 640K, reserved 1048576K quick sort: 355.579434803s merge sort: 355.629940032s insertion sort: 355.532578023s 

TL; DR HotSpot JIT优化消除了计数循环中的安全点检查,这是一个恶意的玩笑。

这是一个非常有趣的问题:一个简单的Java测试揭示了JVM内部的一个非平凡的问题。

没有立即出现线程转储的事实表明问题不在Java代码中,而是以某种方式与JVM相关。 线程转储在安全点打印。 延迟意味着VM无法在短时间内达到安全点。

背景

当没有运行Java线程时,某些VM操作(GC,Deoptimization,Thread dump和其他一些操作 )在stop-the-world暂停时执行。 但Java线程无法在任意点停止,它们只能在某些称为安全点的位置暂停。 在JIT编译的代码中,安全点通常放在方法出口和后向分支,即内部循环中。

安全点检查在性能方面相对便宜,但不是免费的。 这就是JIT编译器尽可能减少安全点数量的原因。 一种这样的优化是消除计数循环中的安全点检查,即具有已知具有有限迭代次数的整数计数器的循环。

validation理论

让我们回到我们的测试并检查是否及时达到安全点。

添加-XX:+SafepointTimeout -XX:SafepointTimeoutDelay=1000 JVM选项。 这应该在VM无法在1000毫秒内达到安全点时打印调试消息。

 # SafepointSynchronize::begin: Timeout detected: # SafepointSynchronize::begin: Timed out while spinning to reach a safepoint. # SafepointSynchronize::begin: Threads which did not reach the safepoint: # "pool-1-thread-2" #12 prio=5 os_prio=0 tid=0x0000000019004800 nid=0x1480 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE # SafepointSynchronize::begin: (End of list) 

是的,它打印出线程pool-1-thread-2无法在1000毫秒内停止。 这是Executor池的第二个线程,它应该运行insertionSort算法。

insertionSort有两个非常长的嵌套计数循环,看起来JVM已经消除了它们内部的安全点检查。 因此,如果此方法在编译模式下运行,则JVM无法在方法完成之前停止它。 如果在方法运行时请求了stop-the-world暂停,则所有其他线程也将等待。

该怎么办?

这个问题早已为人所知。 以下是相关的JVM错误: JDK-5014723 。 它不是一个高优先级的,因为这个问题很少出现在现实生活中。

JDK 8u92中出现了一个新的JVM标志来解决这个问题。
-XX:+UseCountedLoopSafepoints总是将安全点检查放在循环中。

另一种解决方案是通过修改循环内的计数器变量将长计数循环转换为通用循环。

例如,如果你更换if (breaker) break;if (breaker) j = 0; 问题也会消失。

为什么它在调试模式下工作呢?

在启动调试器的情况下启动JVM时,将禁用某些JIT优化以使调试信息可用。 在这种情况下,编译的代码具有所有安全点检查。