ข้าม​ไป​ยัง​เนื้อหา

JEP 491 แก้​ปัญหา pinning ที่​คน​พูด​ถึงกัน​หมด​แล้ว และ​เหลือ​บาน​ที่​ไม่มี​ใคร​พูด​ถึง

virtual thread ถูก​ออกแบบ​มา​ให้​ถอน​ตัว​ออก​จาก carrier thread เวลา​มัน​บล็อก · carrier ตัว​นั้น​จะ​ได้​ไป​ทำงาน​อื่น​ต่อ

มี​ข้อ​ยกเว้น​ข้อ​หนึ่ง​ที่​ดัง​มาก​ตอน virtual thread ออก​ใหม่ ๆ · ถ้า​มัน​บล็อก​ขณะ​ถือ monitor ของ synchronized มัน​ถอน​ตัว​ไม่​ได้ และ carrier ถูก​ตรึง​ไป​ด้วย

JDK 24 แก้​ข้อ​นั้น​แล้ว · หน้า​นี้​ยืนยัน​ว่า​แก้​จริง แล้ว​ชี้​บาน​ที่​ยัง​เหลือ

ตัว​วัด​ปล่อย virtual thread 8 ตัว แล้ว​นับ event jdk.VirtualThreadPinned ที่ JFR อัด​ไว้ · หนึ่ง event ต่อ​การ​ตรึง​หนึ่ง​ครั้ง

JDKบล็อก​เฉย ๆบล็อก​ขณะ​ถือ monitorบล็อก​ใน static initializer
21081
25001
26001

column กลาง​คือ​เรื่อง​ที่​ทุก​คน​รู้ · JDK 21 ตรึง​ครบ​ทุก​เธรด ที่​ปล่อย​ไป และ​ตั้งแต่ 25 เป็นต้น​ไป​เหลือ​ศูนย์

column ขวา​คือ​เรื่อง​ที่​ยัง​ไม่​จบ

virtual thread ที่​ไป​กระตุ้น​ให้ class หนึ่ง​ถูก initialize แล้ว code ใน static initializer ของ class นั้น​บล็อก · carrier ถูก​ตรึง​อยู่ดี

JDKsynchronizedstatic initializer
21ตรึงตรึง
25ไม่​ตรึงตรึง
26ไม่​ตรึงตรึง

เรื่อง​นี้​ไม่​ได้​หา​ยาก · class ที่​อ่าน config ตอน initialize, ที่​เปิด connection pool ไว้​ใน static field, หรือ​ที่​โหลด library ตอน​แรก​สุด · ทุก​อัน​เข้า​ข่าย

และ​มัน​เกิด​ครั้ง​เดียว​ต่อ class ตอน​ที่​มี​คน​แตะ​มัน​เป็น​คน​แรก · ซึ่ง​แปล​ว่า​มัน​จะ​โผล่​ตอน ระบบ​เพิ่ง​ส​ตาร์ต ปน​กับ​ความ​ช้า​อื่น ๆ ที่​คน​คาด​หวัง​อยู่​แล้ว

'virtual thread ไม่ pin แล้ว' เป็น​บท​สรุป​ที่​กว้าง​เกิน​ไป

สิ่ง​ที่ JEP 491 แก้​คือ การ​บล็อก​ขณะ​ถือ monitor ซึ่ง​เป็น​บาน​ที่​พบ​บ่อย​ที่สุด และ​เป็น​บาน​ที่​ทำให้ virtual thread ใช้​กับ library เก่า​ไม่​ได้

สิ่ง​ที่​มัน​ไม่​ได้​แก้​คือ​การ​ตรึง​จาก​เฟรม​ที่ JVM ถอน​ออก​ไม่​ได้ · static initializer เป็น​หนึ่ง​ใน​นั้น และ native frame ก็​เป็น​อีก​หนึ่ง

ถ้า​คุณ​อัป JDK แล้ว​ยัง​เห็น jdk.VirtualThreadPinned ใน​รายงาน · นั่น​ไม่ใช่​ความ​ผิด​พลาด ของ​การ​วัด มัน​คือ​บาน​ที่​เหลือ

หน้า​นี้​เคย​วัด​ผิด​หน่วย และ​นี่​คือ​เหตุผล​ที่​เปลี่ยน

หัวข้อ​ที่​มีชื่อ​ว่า “หน้า​นี้​เคย​วัด​ผิด​หน่วย และ​นี่​คือ​เหตุผล​ที่​เปลี่ยน”

ร่าง​แรก​ของ​ตัว​วัด​ไม่​ได้​นับ event · มัน​ปล่อย virtual thread หลาย​ตัว​โดย​ตั้ง scheduler ให้​มี carrier ตัว​เดียว แล้ว​นับ​ว่า​กี่​ตัว​เดิน​หน้า​ได้​ภายใน​หน้าต่าง​เวลา​หนึ่ง

ได้​ผล​ที่​ถูก คือ 1 จาก 4 บน JDK 21 และ 4 จาก 4 บน​ตัว​ใหม่​กว่า

แต่ หน่วย​นั้น​คือ​เวลา​ที่​ปลอม​ตัว​มา​เป็น​จำนวนนับ · ตัวเลข 1 กับ 4 มา​จาก “กี่​ตัว​ทัน​ภายใน​สาม​วินาที” ซึ่ง​ขึ้น​กับ​ว่า​เครื่อง​ที่​รัน​โหลด​หนัก​แค่​ไหน

การ​นับ event ไม่มี​หน้าต่าง​เวลา​เข้า​มา​เกี่ยว​เลย · โปรแกรม​รัน​จน​จบ แล้ว​อ่าน​ว่า JVM บันทึก​การ​ตรึง​ไว้​กี่​ครั้ง

ศูนย์​ที่​แปล​ว่า​ไม่​ตรึง กับ​ศูนย์​ที่​แปล​ว่า​ไม่​ได้​อัด

ตอน​ตั้ง​ตัว​วัด ผู้​เขียน​ลอง​ส่ง jdk.VirtualThreadPinned#enabled=false เข้าไป​ดู · jfr summary ยัง​พิมพ์​แถว​ของ event นั้น​ออก​มา​พร้อม​เลข 0 เหมือน​เดิม​ทุก​ตัว​อักษร

แปล​ว่า​การ​เช็​กว่า “แถว​มี​อยู่” ไม่ใช่​ช่อง​ควบคุม และ​ศูนย์​ของ JDK 25 จะ​แยก​ไม่​ออก​จาก “JFR ถูก​ปิด​อยู่”

ช่อง​ควบคุม​ที่​ใช้ได้​จริง​คือ column ขวา · มัน​ต้อง​ได้​มากกว่า​ศูนย์​บน​ทุก version ถ้า​มัน​ได้​ศูนย์​เมื่อไร ด่าน​แดง​แล้ว​บอก​ว่า การ​วัด​พัง ไม่ใช่​ว่า​ไม่มี​การ​ตรึง · อีก​ช่อง​คือ column ซ้าย ซึ่ง​เป็น code เดียวกัน​แต่​ไม่​ถือ monitor และ​ต้อง​ได้​ศูนย์​เสมอ

ถ้า​คุณ​อยู่​บน JDK 25 ขึ้น​ไป เลิก​ไล่​ถอด synchronized ออก​เพื่อ​เปลี่ยน​ไป​ใช้ ReentrantLock ได้​แล้ว งาน​นั้น​จบ​ไป​กับ JEP 491

เปิด event jdk.VirtualThreadPinned ไว้​ใน​โปรไฟล์​การ​อัด​ของ production · ค่า​ปริยาย​ของ default.jfc ตั้ง threshold ไว้​ที่ 20 มิลลิ​วินาที ซึ่ง​กรอง การ​ตรึง​สั้น ๆ ทิ้ง​หมด · ตัว​วัด​ของ​หน้า​นี้​ตั้ง​เป็น​ศูนย์ เพราะ​มัน​นับ​จำนวน​ครั้ง ไม่ใช่​วัด​ความ​นาน

เวลา​เห็น event โผล่ ให้​ดู stack trace ของ​มัน​ก่อน​สรุป · ถ้า​ปลาย​ทาง​คือ <clinit> ปัญหา​อยู่​ที่ static initializer ที่​ทำงาน​หนัก​เกิน​ไป ไม่ใช่​ที่ virtual thread

  • ไม่​ได้​วัด​เวลา หน่วย​ของ​หน้า​นี้​คือ​จำนวน event ที่ JVM บันทึก
  • ไม่​ได้​วัด​การ​ตรึง​จาก native frame ซึ่ง​เป็น​อีก​บาน​ที่ JEP 491 ไม่​ได้​แก้ · หน้า​นี้​พูด​ถึง​มัน​แต่​ไม่​ได้​เอา​เลข​มา​แสดง
  • ไม่​ได้​วัดผล​กระทบ​ต่อ throughput การ​ตรึง​หนึ่ง​ครั้ง​จะ​เจ็บ​แค่​ไหน ขึ้น​กับ​ว่า​มี carrier กี่​ตัว​และ​งาน​อื่น​รอ​อยู่​เท่าไร · หน้า​นี้​ตอบ​แค่​ว่า​มัน​เกิด​หรือ​ไม่​เกิด
  • วัด​บน JDK 21, 25, 26 เท่านั้น และ​ทั้งหมด​เป็น build ของ OpenJDK

ตัว​วัด​อยู่​ที่ scripts/vthread-pinning/ รัน​ด้วย npm run verify:vthread-pin · มัน​อยู่​ใน​เกต​ของ CI จึง​มี​คน​รัน​ทุก​ครั้ง​ที่​มี​คน push

ด่าน​บังคับ​ว่า​ต้อง​วัด​คร่อม​เส้น​แบ่ง​ของ JEP 491 · ถ้า​เครื่อง​ไหน​มี JDK ฝั่ง​เดียว ด่าน​แดง​และ​บอก​ว่า​เทียบ​สอง​ฝั่ง​ไม่​ได้ ไม่ใช่​รายงาน​ผล​ครึ่ง​เดียว​แล้ว​บอก​ว่า​ผ่าน