web-dev-qa-db-ja.com

javaコードを実行すると、ブレークポイントなしのデバッグと通常の実行で異なる結果が得られます。ExecutorServiceは壊れていますか?

デバッグのTL:DR ExecutorService executorService = Executors.newFixedThreadPool(8);は同時に実行されますが、通常の実行時には同時実行されますが、後でシングルスレッドで実行されます。

ExecutorServiceで4つの異なるタスクを開始するコードがいくつかあります。これらのタスクのうち2つはほぼ瞬時に終了し、他の2つはしばらく実行する必要があります。

これらのタスクは、実行時間を秒単位で_Future<Double>_で返します。

このコードは、タスクの実行と測定を担当します。

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

後で、これらのタスクを開始した後、同じ入力サイズで前回の実行からの実行時間の順にタスクを印刷します。

_    Future<Double> bubbleSortTime = measure(bubbleSortArray, Solution::bubbleSort, executorService);
    Future<Double> insertionSortTime = measure(insertionSortArray, Solution::insertionSort, executorService);
    Future<Double> quickSortTime = measure(quickSortArray, Solution::quickSort, executorService);
    Future<Double> 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_

通常の実行は異なります。メソッドmeasurelong start = System.nanoTime();が実行された後、スレッドがスリープ状態になり、insertionSortが実行された後、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<Double> measureTime(int[] array, ArraySorter arraySorter, ExecutorService executorService) {
        Callable<Double> 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<Double> bubbleSortTime = measureTime(bubbleSortArray, ThreadedSortingComparsion::bubbleSort, executorService);
        Future<Double> insertionSortTime = measureTime(insertionSortArray, ThreadedSortingComparsion::insertionSort, executorService);
        Future<Double> quickSortTime = measureTime(quickSortArray, ThreadedSortingComparsion::quickSort, executorService);
        Future<Double> 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.length - 1; j++) {
                if (array[j] > array[j + 1]) {
                    swap(array, j + 1, j);
                }
            }
        }
    }

    private static void insertionSort(int[] ar) {
        for (int i = 0; i < ar.length; i++) {
            for (int j = i; j >= 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_92_でテストしました。そして_1.8.0_45_。なぜそれはideoneで動作するのに、私がテストした他の2台の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  <0x0000000719d72480> (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 <0x00000007156892b8> (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 <0x00000007156892b8> (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 <0x0000000719d707e0> (a Java.lang.ref.ReferenceQueue$Lock)
    at Java.lang.ref.ReferenceQueue.remove(ReferenceQueue.Java:143)
    - locked <0x0000000719d707e0> (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 <0x0000000719d78370> (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 <0x0000000719d78370> (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  <0x0000000719d58fe0> (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
_
25
mlecz

TL; DRカウントされたループのセーフポイントチェックを排除するためのHotSpot JIT最適化は、冗談を言っていました。

これは非常に興味深い質問です。単純なJavaテストにより、JVMの奥深くにある重要な問題が明らかになります。

スレッドダンプがすぐに表示されないという事実は、問題がJavaコードにあるのではなく、何らかの形でJVMに関連していることを示しています。スレッドダンプはセーフポイントで印刷されます。遅延は、VMが短時間でセーフポイントに到達できなかったことを意味します。

バックグラウンド

特定のVM操作(GC、最適化解除、スレッドダンプ、および その他 )は、Javaスレッドが実行されていない場合、stop-the-worldポーズで実行されます。ただし、Javaスレッドは任意の時点で停止できず、safepointsと呼ばれる特定の場所でのみ一時停止できます。 JITでコンパイルされたコードでは、セーフポイントは通常、メソッドの出口と後方分岐、つまりループ内に配置されます。

セーフポイントチェックは、パフォーマンスの点では比較的安価ですが、無料ではありません。そのため、JITコンパイラは可能な限りセーフポイントの数を減らしようとします。そのような最適化の1つは、カウントされたループ、つまり、反復回数が有限であることがわかっている整数カウンターを持つループのセーフポイントチェックを排除することです。

理論を検証する

テストに戻り、セーフポイントに間に合うかどうかを確認しましょう。

_-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ミリ秒で停止できなかったことが出力されました。これは、エグゼキュータのプールの2番目のスレッドであり、insertionSortアルゴリズムを実行する必要があります。

insertionSortには2つの非常に長いネストされたカウントループがあり、JVMがそれらの内部のセーフポイントチェックを排除したようです。したがって、このメソッドがコンパイル済みモードで実行されている場合、JVMはメソッドが完了するまでメソッドを停止できません。メソッドの実行中にストップザワールドの一時停止が要求された場合、他のすべてのスレッドも待機します。

何をすべきか?

この問題は長い間知られています。関連するJVMのバグは次のとおりです。 JDK-501472 。実際のアプリケーションでは問題が発生することはめったにないため、優先度の高いものではありません。

この問題を回避するために、新しいJVMフラグがJDK8u92に登場しました。
_-XX:+UseCountedLoopSafepoints_は常にセーフポイントチェックをループ内に配置します。

別の解決策は、ループ内のカウンター変数を変更することにより、長いカウントのループをジェネリックループに変換することです。

例えば。 if (breaker) break;if (breaker) j = 0;に置き換えると、問題も解消されます。

では、なぜデバッグモードで動作するのですか?

デバッガーをオンにしてJVMを起動すると、デバッグ情報を利用できるようにするために一部のJIT最適化が無効になります。この場合、コンパイルされたコードにはすべてのセーフポイントチェックがあります。

38
apangin