เธรด Java ที่ดำเนินการส่วนที่เหลือในลูปบล็อกเธรดอื่น ๆ ทั้งหมด


123

ข้อมูลโค้ดต่อไปนี้เรียกใช้เธรดสองเธรดหนึ่งคือการบันทึกตัวจับเวลาอย่างง่ายทุกวินาทีวินาทีคือลูปแบบไม่มีที่สิ้นสุดที่รันการดำเนินการที่เหลือ:

public class TestBlockingThread {
    private static final Logger LOGGER = LoggerFactory.getLogger(TestBlockingThread.class);

    public static final void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            int i = 0;
            while (true) {
                i++;
                if (i != 0) {
                    boolean b = 1 % i == 0;
                }
            }
        };

        new Thread(new LogTimer()).start();
        Thread.sleep(2000);
        new Thread(task).start();
    }

    public static class LogTimer implements Runnable {
        @Override
        public void run() {
            while (true) {
                long start = System.currentTimeMillis();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // do nothing
                }
                LOGGER.info("timeElapsed={}", System.currentTimeMillis() - start);
            }
        }
    }
}

สิ่งนี้ให้ผลลัพธ์ดังต่อไปนี้:

[Thread-0] INFO  c.m.c.concurrent.TestBlockingThread - timeElapsed=1004
[Thread-0] INFO  c.m.c.concurrent.TestBlockingThread - timeElapsed=1003
[Thread-0] INFO  c.m.c.concurrent.TestBlockingThread - timeElapsed=13331
[Thread-0] INFO  c.m.c.concurrent.TestBlockingThread - timeElapsed=1006
[Thread-0] INFO  c.m.c.concurrent.TestBlockingThread - timeElapsed=1003
[Thread-0] INFO  c.m.c.concurrent.TestBlockingThread - timeElapsed=1004
[Thread-0] INFO  c.m.c.concurrent.TestBlockingThread - timeElapsed=1004

ฉันไม่เข้าใจว่าทำไมงานที่ไม่มีที่สิ้นสุดจึงบล็อกเธรดอื่น ๆ ทั้งหมดเป็นเวลา 13.3 วินาที ฉันพยายามเปลี่ยนลำดับความสำคัญของเธรดและการตั้งค่าอื่น ๆ แล้วไม่ได้ผล

หากคุณมีข้อเสนอแนะในการแก้ไขปัญหานี้ (รวมถึงการปรับแต่งการตั้งค่าการสลับบริบทของระบบปฏิบัติการ) โปรดแจ้งให้เราทราบ


8
@Marthin ไม่ใช่ GC. มันคือ JIT ทำงานโดย-XX:+PrintCompilationฉันได้รับสิ่งต่อไปนี้ในเวลาที่การหน่วงเวลาขยายสิ้นสุดลง: TestBlockingThread :: lambda $ 0 @ 2 (24 bytes) COMPILE SKIPPED: trivial infinite loop (ลองอีกครั้งในระดับที่แตกต่างกัน)
Andreas

4
มันสร้างซ้ำในระบบของฉันโดยมีการเปลี่ยนแปลงเพียงอย่างเดียวคือฉันแทนที่การโทรบันทึกด้วย System.out.println ดูเหมือนจะเป็นปัญหาของตัวกำหนดตารางเวลาเพราะถ้าคุณแนะนำ 1ms sleep ภายในของ Runnable's while (true) ลูปการหยุดชั่วคราวในเธรดอื่นจะหายไป
JJF

3
ไม่ใช่ว่าฉันแนะนำ แต่ถ้าคุณปิดใช้งาน JIT โดยใช้-Djava.compiler=NONEมันจะไม่เกิดขึ้น
Andreas

3
คุณสามารถปิดใช้งาน JIT ได้ด้วยวิธีการเดียว ดูปิดการใช้งาน Java JIT สำหรับเมธอด / คลาสเฉพาะหรือไม่
Andreas

3
ไม่มีการหารจำนวนเต็มในรหัสนี้ โปรดแก้ไขชื่อและคำถามของคุณ
Marquis of Lorne

คำตอบ:


94

หลังจากคำอธิบายทั้งหมดที่นี่ (ขอบคุณPeter Lawrey ) เราพบว่าแหล่งที่มาหลักของการหยุดชั่วคราวนี้คือจุดที่ปลอดภัยในลูปนั้นเข้าถึงได้ค่อนข้างน้อยดังนั้นจึงใช้เวลานานในการหยุดเธรดทั้งหมดสำหรับการเปลี่ยนโค้ดที่คอมไพล์ด้วย JIT

แต่ฉันตัดสินใจที่จะลงลึกและพบว่าเหตุใดจึงเข้าถึงจุดปลอดภัยได้น้อยครั้ง ฉันพบว่ามันค่อนข้างสับสนเล็กน้อยว่าทำไมการกระโดดย้อนกลับwhileจึงไม่ "ปลอดภัย" ในกรณีนี้

ดังนั้นฉันจึงเรียก-XX:+PrintAssemblyมันด้วยรัศมีภาพทั้งหมดเพื่อช่วย

-XX:+UnlockDiagnosticVMOptions \
-XX:+TraceClassLoading \
-XX:+DebugNonSafepoints \
-XX:+PrintCompilation \
-XX:+PrintGCDetails \
-XX:+PrintStubCode \
-XX:+PrintAssembly \
-XX:PrintAssemblyOptions=-Mintel

หลังจากการตรวจสอบบางอย่างฉันพบว่าหลังจากคอมC2ไพเลอร์แลมบ์ดาคอมไพเลอร์ใหม่ครั้งที่สามได้โยนโพลเซฟพอยต์ภายในลูปออกไปจนหมด

UPDATE

ในระหว่างตัวแปรขั้นตอนการทำโปรไฟล์iไม่เคยเห็นว่าเท่ากับ 0 นั่นเป็นเหตุผลที่C2ทำให้สาขานี้ถูกปรับให้เหมาะสมโดยเฉพาะเพื่อให้ลูปถูกเปลี่ยนเป็นบางอย่างเช่น

for (int i = OSR_value; i != 0; i++) {
    if (1 % i == 0) {
        uncommon_trap();
    }
}
uncommon_trap();

สังเกตว่าเดิมทีการวนซ้ำแบบไม่มีที่สิ้นสุดถูกเปลี่ยนรูปแบบเป็นวง จำกัด ปกติโดยมีตัวนับ! เนื่องจากการเพิ่มประสิทธิภาพ JIT เพื่อกำจัดการสำรวจความปลอดภัยในลูปที่นับจำนวน จำกัด จึงไม่มีการสำรวจความปลอดภัยในลูปนี้เช่นกัน

หลังจากนั้นไม่นานiห่อกลับไป0และกับดักที่ผิดปกติถูกนำมาใช้ วิธีนี้ถูกลดประสิทธิภาพและดำเนินการต่อในล่าม ในระหว่างการคอมไพล์ใหม่ด้วยความรู้ใหม่C2ได้รับรู้ถึงลูปที่ไม่มีที่สิ้นสุดและเลิกรวบรวม ส่วนที่เหลือของวิธีการดำเนินการในล่ามโดยมีจุดปลอดภัยที่เหมาะสม

มีบล็อกโพสต์ที่ยอดเยี่ยมที่ต้องอ่าน"Safepoints: ความหมายผลข้างเคียงและค่าโสหุ้ย"โดยNitsan Wakartซึ่งครอบคลุมจุดปลอดภัยและประเด็นเฉพาะนี้

การกำจัด Safepoint ในการนับลูปที่ยาวมากเป็นที่ทราบกันดีว่าเป็นปัญหา ข้อบกพร่องJDK-5014723(ขอบคุณVladimir Ivanov ) ช่วยแก้ปัญหานี้

วิธีแก้ปัญหาจะพร้อมใช้งานจนกว่าข้อบกพร่องจะได้รับการแก้ไขในที่สุด

  1. คุณสามารถลองใช้-XX:+UseCountedLoopSafepoints( จะทำให้เกิดการลงโทษประสิทธิภาพโดยรวมและอาจทำให้ JVM ขัดข้อง JDK-8161147 ) หลังจากใช้C2คอมไพเลอร์แล้วยังคงรักษาจุดปลอดภัยไว้ที่การกระโดดด้านหลังและการหยุดชั่วคราวดั้งเดิมจะหายไปอย่างสมบูรณ์
  2. คุณสามารถปิดใช้งานการรวบรวมวิธีการที่มีปัญหาอย่างชัดเจนโดยใช้
    -XX:CompileCommand='exclude,binary/class/Name,methodName'

  3. หรือคุณสามารถเขียนโค้ดของคุณใหม่โดยเพิ่มจุดปลอดภัยด้วยตนเอง ตัวอย่างเช่นการThread.yield()โทรเมื่อสิ้นสุดรอบหรือแม้กระทั่งเปลี่ยนint iเป็นlong i(ขอบคุณNitsan Wakart ) ก็จะแก้ไขการหยุดชั่วคราว


7
นี่คือคำตอบที่แท้จริงสำหรับคำถามว่าจะแก้ไขอย่างไร
Andreas

คำเตือน: อย่าใช้-XX:+UseCountedLoopSafepointsในการผลิตเพราะมันอาจจะผิดพลาด JVM วิธีแก้ปัญหาที่ดีที่สุดคือการแบ่งห่วงยาวออกเป็นวงที่สั้นกว่าด้วยตนเอง
apangin

@apangin อ่า. เข้าใจแล้ว! ขอบคุณ :) ดังนั้นจึงc2ลบจุดปลอดภัยออก! แต่อีกสิ่งหนึ่งที่ฉันไม่ได้รับคือสิ่งที่จะเกิดขึ้นต่อไป เท่าที่ฉันเห็นไม่มีจุดปลอดภัยเหลืออยู่หลังจากคลายลูป (?) และดูเหมือนว่าจะไม่มีทางทำ stw จึงมีการหมดเวลาบางอย่างเกิดขึ้นและมีการยกเลิกการเพิ่มประสิทธิภาพ?
vsminkov

2
ความคิดเห็นก่อนหน้าของฉันไม่ถูกต้อง ตอนนี้ชัดเจนแล้วว่าเกิดอะไรขึ้น ในขั้นตอนการทำโปรไฟล์iจะไม่เป็น 0 ดังนั้นลูปจึงถูกแปลงอย่างละเอียดเป็นบางอย่างเช่นfor (int i = osr_value; i != 0; i++) { if (1 % i == 0) uncommon_trap(); } uncommon_trap();Ie เป็นลูปที่นับแบบ จำกัด ปกติ เมื่อรวมiกลับเป็น 0 แล้วจะใช้กับดักที่ผิดปกติวิธีนี้จะถูกลดประสิทธิภาพและดำเนินการในล่าม ในระหว่างการคอมไพล์ใหม่ด้วยความรู้ใหม่ JIT จะรับรู้การวนซ้ำที่ไม่มีที่สิ้นสุดและยอมแพ้การคอมไพล์ ส่วนที่เหลือของวิธีการดำเนินการในล่ามด้วยจุดปลอดภัยที่เหมาะสม
apangin

1
คุณสามารถสร้าง ia ให้ยาวแทนที่จะเป็น int ซึ่งจะทำให้ลูป "ไม่นับ" และแก้ปัญหาได้
Nitsan Wakart

64

ในระยะสั้นห่วงที่คุณไม่มีจุดปลอดภัยอยู่ข้างในยกเว้นเมื่อi == 0ถึง เมื่อวิธีนี้ถูกคอมไพล์และทริกเกอร์โค้ดที่จะถูกแทนที่จำเป็นต้องนำเธรดทั้งหมดไปยังจุดปลอดภัย แต่ต้องใช้เวลานานมากการล็อกไม่เพียง แต่เธรดที่รันโค้ด แต่เธรดทั้งหมดใน JVM

ฉันเพิ่มตัวเลือกบรรทัดคำสั่งต่อไปนี้

-XX:+PrintGCApplicationStoppedTime -XX:+PrintGCApplicationConcurrentTime -XX:+PrintCompilation

ฉันยังแก้ไขโค้ดให้ใช้ทศนิยมซึ่งดูเหมือนจะใช้เวลานานกว่านี้

boolean b = 1.0 / i == 0;

และสิ่งที่ฉันเห็นในผลลัพธ์คือ

timeElapsed=100
Application time: 0.9560686 seconds
  41423  280 %     4       TestBlockingThread::lambda$main$0 @ -2 (27 bytes)   made not entrant
Total time for which application threads were stopped: 40.3971116 seconds, Stopping threads took: 40.3967755 seconds
Application time: 0.0000219 seconds
Total time for which application threads were stopped: 0.0005840 seconds, Stopping threads took: 0.0000383 seconds
  41424  281 %     3       TestBlockingThread::lambda$main$0 @ 2 (27 bytes)
timeElapsed=40473
  41425  282 %     4       TestBlockingThread::lambda$main$0 @ 2 (27 bytes)
  41426  281 %     3       TestBlockingThread::lambda$main$0 @ -2 (27 bytes)   made not entrant
timeElapsed=100

หมายเหตุ: หากต้องการเปลี่ยนโค้ดเธรดจะต้องหยุดที่จุดปลอดภัย อย่างไรก็ตามที่นี่ดูเหมือนว่าจะถึงจุดปลอดภัยนั้นน้อยมาก (อาจเป็นได้ก็ต่อเมื่อi == 0เปลี่ยนภารกิจเป็น

Runnable task = () -> {
    for (int i = 1; i != 0 ; i++) {
        boolean b = 1.0 / i == 0;
    }
};

ฉันเห็นความล่าช้าที่คล้ายกัน

timeElapsed=100
Application time: 0.9587419 seconds
  39044  280 %     4       TestBlockingThread::lambda$main$0 @ -2 (28 bytes)   made not entrant
Total time for which application threads were stopped: 38.0227039 seconds, Stopping threads took: 38.0225761 seconds
Application time: 0.0000087 seconds
Total time for which application threads were stopped: 0.0003102 seconds, Stopping threads took: 0.0000105 seconds
timeElapsed=38100
timeElapsed=100

การเพิ่มรหัสลงในลูปอย่างระมัดระวังคุณจะได้รับความล่าช้านานขึ้น

for (int i = 1; i != 0 ; i++) {
    boolean b = 1.0 / i / i == 0;
}

ได้รับ

 Total time for which application threads were stopped: 59.6034546 seconds, Stopping threads took: 59.6030773 seconds

อย่างไรก็ตามเปลี่ยนรหัสเพื่อใช้วิธีดั้งเดิมที่มีจุดปลอดภัยเสมอ (หากไม่ใช่ภายใน)

for (int i = 1; i != 0 ; i++) {
    boolean b = Math.cos(1.0 / i) == 0;
}

พิมพ์

Total time for which application threads were stopped: 0.0001444 seconds, Stopping threads took: 0.0000615 seconds

หมายเหตุ: การเพิ่มif (Thread.currentThread().isInterrupted()) { ... }ในลูปจะเพิ่มจุดปลอดภัย

หมายเหตุ: สิ่งนี้เกิดขึ้นบนเครื่อง 16 คอร์ดังนั้นจึงไม่มีทรัพยากร CPU ขาด


1
มันเป็นจุดบกพร่องของ JVM ใช่ไหม? โดยที่ "ข้อบกพร่อง" หมายถึงปัญหาคุณภาพการใช้งานที่รุนแรงและไม่ละเมิดข้อกำหนด
usr

1
@vsminkov สามารถหยุดโลกได้หลายนาทีเนื่องจากไม่มีจุดปลอดภัยดูเหมือนว่ามันควรได้รับการปฏิบัติเหมือนแมลง รันไทม์มีหน้าที่แนะนำจุดปลอดภัยเพื่อหลีกเลี่ยงการรอนาน
Voo

1
@Voo แต่ในทางกลับกันการรักษาจุดปลอดภัยในทุก ๆ การกระโดดกลับอาจทำให้วงจรซีพียูเสียค่าใช้จ่ายเป็นจำนวนมากและทำให้ประสิทธิภาพการทำงานของแอปพลิเคชันทั้งหมดลดลงอย่างเห็นได้ชัด แต่ฉันเห็นด้วยกับคุณ ในกรณีนั้นมันดูถูกกฎหมายที่จะรักษาจุดปลอดภัยไว้
vsminkov

9
@Voo well ... ฉันจำภาพนี้ได้เสมอเมื่อพูดถึงการเพิ่มประสิทธิภาพ: D
vsminkov

1
.NET แทรกจุดปลอดภัยที่นี่ (แต่. NET มีโค้ดที่สร้างช้า) วิธีแก้ไขที่เป็นไปได้คือการวนซ้ำ แยกออกเป็นสองลูปทำให้ด้านในไม่ตรวจสอบแบทช์ของ 1024 องค์ประกอบและลูปด้านนอกจะขับเคลื่อนแบตช์และเซฟพอยต์ ลดค่าใช้จ่ายตามแนวความคิด 1024x ซึ่งน้อยกว่าในทางปฏิบัติ
usr

26

พบคำตอบของเหตุผล พวกเขาเรียกว่าจุดปลอดภัยและเป็นที่รู้จักกันดีในชื่อ Stop-The-World ที่เกิดขึ้นเนื่องจาก GC

ดูบทความนี้: การบันทึกหยุดโลกหยุดชั่วคราวใน JVM

เหตุการณ์ที่แตกต่างกันอาจทำให้ JVM หยุดเธรดแอ็พพลิเคชันทั้งหมดชั่วคราว การหยุดดังกล่าวเรียกว่า Stop-The-World (STW) หยุดชั่วคราว สาเหตุที่พบบ่อยที่สุดสำหรับการหยุด STW ที่ถูกทริกเกอร์คือการรวบรวมขยะ (ตัวอย่างใน github) แต่การดำเนินการ JIT ที่แตกต่างกัน(ตัวอย่าง) การยกเลิกการล็อกแบบเอนเอียง (ตัวอย่าง) การดำเนินการ JVMTI บางอย่างและอื่น ๆ อีกมากมายจำเป็นต้องให้แอปพลิเคชันหยุดทำงาน

จุดที่เธรดของแอปพลิเคชันอาจหยุดได้อย่างปลอดภัยถูกเรียกว่าประหลาดใจจุดที่ปลอดภัย คำนี้มักใช้เพื่ออ้างถึงการหยุด STW ทั้งหมด

เป็นเรื่องปกติมากหรือน้อยที่จะเปิดใช้บันทึก GC อย่างไรก็ตามสิ่งนี้ไม่ได้รวบรวมข้อมูลเกี่ยวกับจุดปลอดภัยทั้งหมด เพื่อให้ได้ทั้งหมดใช้ตัวเลือก JVM เหล่านี้:

-XX:+PrintGCApplicationStoppedTime -XX:+PrintGCApplicationConcurrentTime

หากคุณสงสัยเกี่ยวกับการตั้งชื่อที่อ้างถึง GC อย่างชัดเจนอย่าเพิ่งตื่นตระหนกเพราะการเปิดตัวเลือกเหล่านี้จะบันทึกจุดปลอดภัยทั้งหมดไม่ใช่แค่การเก็บขยะหยุดชั่วคราว หากคุณรันตัวอย่างต่อไปนี้ (ซอร์สใน github) ด้วยแฟล็กที่ระบุด้านบน

การอ่านอภิธานศัพท์ของ HotSpotจะกำหนดสิ่งนี้:

SafePoint

จุดระหว่างการเรียกใช้งานโปรแกรมซึ่งรู้ราก GC ทั้งหมดและเนื้อหาของฮีปอ็อบเจ็กต์ทั้งหมดสอดคล้องกัน จากมุมมองทั่วโลกเธรดทั้งหมดต้องปิดกั้นที่จุดปลอดภัยก่อนที่ GC จะทำงานได้ (ในกรณีพิเศษเธรดที่รันโค้ด JNI สามารถทำงานต่อไปได้เนื่องจากใช้เฉพาะจุดจับในระหว่างจุดปลอดภัยพวกเขาต้องบล็อกแทนการโหลดเนื้อหาของจุดจับ) จากมุมมองในพื้นที่จุดปลอดภัยคือจุดที่แตกต่าง ในบล็อกของรหัสที่เธรดการดำเนินการอาจบล็อกสำหรับ GC ไซต์การโทรส่วนใหญ่มีคุณสมบัติเป็นจุดปลอดภัยมีค่าคงที่ที่ชัดเจนซึ่งถือเป็นจริงในทุกจุดปลอดภัยซึ่งอาจถูกมองข้ามไปที่จุดที่ไม่ใช่จุดปลอดภัย ทั้งโค้ด Java ที่คอมไพล์และโค้ด C / C ++ ได้รับการปรับให้เหมาะสมระหว่างจุดปลอดภัย แต่จะน้อยกว่าในเซฟพอยต์ คอมไพเลอร์ JIT แสดงแผนที่ GC ที่จุดปลอดภัยแต่ละจุด โค้ด C / C ++ ใน VM ใช้รูปแบบการใช้มาโครที่มีสไตล์ (เช่น TRAPS) เพื่อทำเครื่องหมายจุดปลอดภัยที่เป็นไปได้

ทำงานด้วยแฟล็กที่กล่าวถึงข้างต้นฉันได้ผลลัพธ์นี้:

Application time: 0.9668750 seconds
Total time for which application threads were stopped: 0.0000747 seconds, Stopping threads took: 0.0000291 seconds
timeElapsed=1015
Application time: 1.0148568 seconds
Total time for which application threads were stopped: 0.0000556 seconds, Stopping threads took: 0.0000168 seconds
timeElapsed=1015
timeElapsed=1014
Application time: 2.0453971 seconds
Total time for which application threads were stopped: 10.7951187 seconds, Stopping threads took: 10.7950774 seconds
timeElapsed=11732
Application time: 1.0149263 seconds
Total time for which application threads were stopped: 0.0000644 seconds, Stopping threads took: 0.0000368 seconds
timeElapsed=1015

สังเกตเห็นเหตุการณ์ STW ที่สาม:
เวลาทั้งหมดที่หยุด: 10.7951187 วินาทีการ
หยุดเธรดใช้เวลา: 10.7950774 วินาที

JIT เองแทบจะใช้เวลาไม่นาน แต่เมื่อ JVM ตัดสินใจที่จะทำการคอมไพล์ JIT มันจะเข้าสู่โหมด STW อย่างไรก็ตามเนื่องจากโค้ดที่จะคอมไพล์ (ลูปไม่มีที่สิ้นสุด) ไม่มีไซต์การโทรจึงไม่มีจุดปลอดภัย

STW สิ้นสุดลงเมื่อ JIT เลิกรอในที่สุดและสรุปว่าโค้ดอยู่ในวงวนที่ไม่มีที่สิ้นสุด


"Safepoint - จุดระหว่างการทำงานของโปรแกรมซึ่งทราบราก GC ทั้งหมดและเนื้อหาของฮีปวัตถุทั้งหมดมีความสอดคล้องกัน" - เหตุใดสิ่งนี้จึงไม่เป็นจริงในลูปที่กำหนด / อ่านตัวแปรชนิดค่าในเครื่องเท่านั้น
BlueRaja - Danny Pflughoeft

@ BlueRaja-DannyPflughoeft ฉันพยายามตอบคำถามนี้ในคำตอบของฉัน
vsminkov

5

หลังจากติดตามเธรดความคิดเห็นและการทดสอบบางอย่างด้วยตัวเองฉันเชื่อว่าการหยุดชั่วคราวเกิดจากคอมไพเลอร์ JIT ทำไมคอมไพเลอร์ JIT ใช้เวลานานขนาดนี้จึงเกินความสามารถในการดีบัก

อย่างไรก็ตามเนื่องจากคุณขอเพียงวิธีป้องกันสิ่งนี้ฉันจึงมีวิธีแก้ไข:

ดึงการวนซ้ำที่ไม่มีที่สิ้นสุดของคุณเข้าสู่วิธีการที่สามารถแยกออกจากคอมไพเลอร์ JIT

public class TestBlockingThread {
    private static final Logger LOGGER = Logger.getLogger(TestBlockingThread.class.getName());

    public static final void main(String[] args) throws InterruptedException     {
        Runnable task = () -> {
            infLoop();
        };
        new Thread(new LogTimer()).start();
        Thread.sleep(2000);
        new Thread(task).start();
    }

    private static void infLoop()
    {
        int i = 0;
        while (true) {
            i++;
            if (i != 0) {
                boolean b = 1 % i == 0;
            }
        }
    }

รันโปรแกรมของคุณด้วยอาร์กิวเมนต์ VM นี้:

-XX: CompileCommand = exclude, PACKAGE.TestBlockingThread :: infLoop (แทนที่ PACKAGE ด้วยข้อมูลแพ็คเกจของคุณ)

คุณควรได้รับข้อความเช่นนี้เพื่อระบุว่าเมื่อใดที่เมธอดจะถูกคอมไพล์ JIT:
### ไม่รวมคอมไพล์: การบล็อกแบบคงที่ TestBlockingThread :: infLoop
คุณอาจสังเกตเห็นว่าฉันใส่คลาสลงในแพ็คเกจที่เรียกว่าการบล็อก


1
คอมไพเลอร์ใช้เวลาไม่นานปัญหาคือโค้ดไม่ถึงจุดปลอดภัยเพราะไม่มีใครอยู่ในลูปยกเว้นเมื่อi == 0
Peter Lawrey

@PeterLawrey แต่ทำไม end of cycle in whileloop ถึงไม่ปลอดภัย?
vsminkov

@vsminkov ดูเหมือนว่าจะมีจุดปลอดภัยif (i != 0) { ... } else { safepoint(); }แต่หายากมาก กล่าวคือ หากคุณออกจาก / ทำลายลูปคุณจะได้เวลาเท่าเดิม
Peter Lawrey

@PeterLawrey หลังจากการตรวจสอบเล็กน้อยฉันพบว่ามันเป็นเรื่องธรรมดาที่จะสร้างจุดปลอดภัยในการกระโดดกลับ ฉันแค่อยากรู้ว่าความแตกต่างในกรณีนี้คืออะไร บางทีฉันอาจจะไร้เดียงสา แต่ฉันไม่เห็นเหตุผลว่าทำไมการกระโดดกลับไม่ "ปลอดภัย"
vsminkov

@vsminkov ฉันสงสัยว่า JIT เห็นจุดปลอดภัยอยู่ในวงจึงไม่ได้เพิ่มหนึ่งจุดในตอนท้าย
Peter Lawrey
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.