1 คะแนน โดย GN⁺ 2024-11-12 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • ซีรีส์มินิโพสต์แบบต่อเนื่องที่แยกความรู้ภายใน JVM ออกเป็น หน่วยเล็กๆ เพื่ออธิบาย โดยแต่ละบทความจะโฟกัสที่หัวข้อ การทดสอบ เบนช์มาร์ก หรือข้อสังเกตเพียงอย่างเดียว
  • แต่ละโพสต์ตั้งเป้าให้อ่านจบได้ภายใน 5–10 นาที และตั้งอยู่บนสมมติฐานว่าองค์ประกอบต่างๆ ของ JVM มีปฏิสัมพันธ์ต่อกัน
  • หลักฐานและการอภิปรายอาจมีลักษณะเป็น ประสบการณ์เฉพาะกรณี และอาจยังไม่ได้รับการตรวจทานเรื่องข้อผิดพลาด ความสม่ำเสมอ สำนวน ไวยากรณ์ และความซ้ำซ้อนอย่างเพียงพอ จึงควรใช้ความระมัดระวังหากจะเชื่อถือโดยตรง
  • ชุดรวมทั้งหมดมีให้ในรูปแบบ ePUB, MOBI, PDF โดย PDF มีขนาดระดับหลายสิบ MB เนื่องจากเป็นการแปลงคุณภาพสูง
  • ดัชนีบทความรายชิ้นแบ่งเป็นหมวด Compiler, Runtime, GC และ Library ครอบคลุมหัวข้อภายใน JVM เช่น การเพิ่มประสิทธิภาพล็อก, TLAB, การหยุดทำงานของ GC, String.intern(), safepoint, compressed references และ conditional moves

ข้อสมมติเมื่ออ่านซีรีส์นี้

  • JVM Anatomy Quarks เป็นซีรีส์มินิโพสต์แบบต่อเนื่องที่สรุปความรู้พื้นฐานของ JVM ในรูปแบบบทความสั้น
  • แต่ละโพสต์เป็นงานเขียนที่เจาะลึกไปยัง หัวข้อ การทดสอบ เบนช์มาร์ก หรือข้อสังเกตเพียงหนึ่งอย่าง
  • หากอ่านเฉพาะโพสต์เดี่ยวๆ อาจขาดบริบท และองค์ประกอบส่วนใหญ่ที่กล่าวถึงมัก มีปฏิสัมพันธ์ ต่อกันได้ง่าย
  • หลักฐานและการอภิปรายในบทความอาจเป็นเพียงประสบการณ์เฉพาะกรณี และอาจยังไม่ได้ผ่านการตรวจทานเรื่องข้อผิดพลาด ความสม่ำเสมอ สำนวน ไวยากรณ์ ความหมาย และความซ้ำซ้อนอย่างเพียงพอ
  • หากจะนำเนื้อหาไปใช้หรือเชื่อถือ ผู้อ่านต้องยอมรับความเสี่ยงด้วยตนเอง

ไฟล์ชุดรวมและโครงสร้างดัชนี

  • ชุดรวมของซีรีส์ทั้งหมดมีให้ในสามรูปแบบ
    • ePUB มีขนาดเล็กที่สุด ไม่ถึง 1 MB และสร้างบนพื้นฐาน Pandoc HTML-to-ePUB
    • MOBI มีขนาดเล็ก ระดับ MB และสร้างบนพื้นฐาน KindleGen ePUB-to-MOBI
    • PDF มีขนาดใหญ่มาก ระดับหลายสิบ MB และเป็นผลลัพธ์คุณภาพสูงจาก wkhtmltopdf HTML-to-PDF
  • ดัชนีรายชิ้นประกอบด้วยหมวด Compiler, Runtime, GC, Library

รายการบทความตามหัวข้อ

  • หมวดที่เน้น Compiler

    • #1: Lock Coarsening and Loops
    • #14: Constant Variables
    • #15: Just-In-Time Constants
    • #16: Megamorphic Virtual Calls
    • #17: Trust Non-Static Final Fields
    • #18: Scalar Replacement
    • #19: Lock Elision
    • #20: FPU Spills
    • #25: Implicit Null Checks
    • #27: Compiler Blackholes
    • #28: Frequency-Based Code Layout
    • #29: Uncommon Traps
    • #30: Conditional Moves
  • หมวดที่เกี่ยวข้องทั้ง Runtime และ GC

    • เป็นบทความที่ว่าด้วย หน่วยความจำและพฤติกรรมการหยุดชั่วคราว ระหว่างการทำงานของ JVM
    • #2: Transparent Huge Pages
    • #4: TLAB Allocation
    • #5: TLABs and Heap Parsability
    • #6: New Object Stages
    • #7: Object Initialization Costs
    • #9: JNI Critical and GC Locker
    • #22: Safepoint Polls
  • หมวดที่เน้น GC

    • เน้นเรื่องการออกแบบตัวเก็บกวาดและพฤติกรรมของฮีป
    • #3: GC Design and Pauses
    • #11: Moving GC and Locality
    • #13: Intergenerational Barriers
    • #21: Heap Uncommit
  • หมวดที่เน้น Runtime

    • เป็นบทความที่ว่าด้วยสภาพแวดล้อมการทำงานของ JVM และการแทนวัตถุ
    • #12: Native Memory Tracking
    • #23: Compressed References
    • #24: Object Alignment
    • #26: Identity Hash Code
  • หมวด Library หรือหมวดผสม

    • หมวดที่รวม Library มี #10: String.intern()
    • หมวดที่ระบุทั้ง Compiler และ Runtime ได้แก่ #16, #25, #29, #30

1 ความคิดเห็น

 
GN⁺ 2024-11-12
ความคิดเห็นจาก Hacker News
  • https://shipilev.net/jvm/anatomy-quarks/17-trust-nonstatic-f... เป็นกรณีที่น่าเสียดายจริง ๆ
    เพราะบางเฟรมเวิร์กใช้ JNI และ reflection ในทางที่เกินควรเพื่อเปลี่ยนฟิลด์ final ที่เดิมควรจะไม่เปลี่ยนแปลงได้ ทำให้โค้ดของผู้ใช้พลาด การปรับแต่งประสิทธิภาพที่สำคัญ ซึ่งทำได้เฉพาะกับคลาสที่ระบบจัดเตรียมให้เท่านั้น
    แพลตฟอร์ม โดยเฉพาะคอมไพเลอร์และรันไทม์ ควรบังคับใช้ ข้อจำกัดเชิงความหมาย อย่างเข้มงวดมาก เพื่อรักษาพื้นที่สำหรับการปรับแต่งประสิทธิภาพในอนาคต

    • ในฐานะส่วนหนึ่งของกลยุทธ์ integrity by default [1] เรากำลังเปลี่ยนเรื่องนี้ และ JEP ที่เกี่ยวข้องน่าจะออกมาเร็ว ๆ นี้
      โค้ดที่จำเป็นต้องเปลี่ยน final จริง ๆ มีไม่มาก และตอนนี้งานนั้นก็ถูกจำกัดไว้กับคลาสภายในโมดูลของตัวเอง หรือคลาสที่ถูก open อย่างชัดเจนอยู่แล้ว ดังนั้นในอนาคตจะเป็นรูปแบบที่แอปพลิเคชันต้องให้สิทธิ์แก่โมดูลที่พยายามเปลี่ยน final
      คล้ายกับแนวทางที่เพิ่งนำไปใช้กับการเรียก native และการเข้าถึงหน่วยความจำแบบ unsafe
      [1]: https://openjdk.org/jeps/8305968
    • รู้สึกเหมือนบางคนทำตาม Effective Java แบบไม่ลืมหูลืมตา จนกลายเป็นบาปกำเนิดแบบว่า ใส่ final ให้ทุกอย่าง หรือเปล่า
      ผลคือทุกวันนี้เราไม่สามารถ mock คลาส final ในการทดสอบได้ง่าย ๆ และเครื่องมือ mock ถึงขั้นต้อง ดัดแปลง bytecode เพื่อ mock คลาส final
      ตัวอย่างเช่น ภายใน Google นั้น Effective Java เป็นข้อกำหนด จึงมีคลาส final แม้แต่ใน GDrive API สาธารณะ ทั้งที่ API ภายนอกนี่แหละคือสิ่งที่อยาก mock มากที่สุด
    • กรณี System.out เป็นปัญหาของ Java เอง
      https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.htm...
      เมื่อมีบรรทัดฐานแบบนี้ ก็ไม่น่าแปลกใจที่คนอื่นจะคิดว่านี่เป็นวิธีที่ยอมรับได้
    • ตัวแก้ไขฟิลด์ไม่ใช่ ข้อจำกัดด้านความปลอดภัย แต่เป็นข้อจำกัดเชิงความหมาย
      หากผ่านขั้นตอนการเลี่ยงที่เหมาะสม ก็ควรจะเลี่ยงได้
      ประเด็นสำคัญคือความปลอดภัย เพราะการเปลี่ยนสิ่งที่ไม่ควรแก้ไขได้อาจทำให้เกิด SEGV และนั่นคือข้อกังวลที่ access modifier พยายามจัดการ
    • ยอมรับว่าเคยเขียน โค้ดสามบรรทัดเจตนาร้าย ด้วยตัวเองเมื่อนานมาแล้ว เพื่อ “un-final” สมาชิกในไลบรารีภายในที่ตอนนี้หายไปแล้ว เปลี่ยนค่า แล้วทำให้มันเหมือน final อีกครั้ง เพื่อหลีกเลี่ยงการรีแฟกเตอร์ที่น่าปวดหัว
      ถ้าถูกห้ามไว้ก็คงดี แต่ตอนนั้นมีความต้องการทางธุรกิจ
  • ออกนอกเรื่องเล็กน้อย แต่ Apple เพิ่งปล่อย Swift Java bridge ตัวใหม่ และมันเจ๋งทีเดียว
    รองรับทั้ง JNI และ Panama และเมื่อสัปดาห์ก่อนก็กำลังพอร์ตมันไปยัง Android อยู่
    https://github.com/swiftlang/swift-java

    • ถ้าจะเรียกว่า “สมัยใหม่” ได้ แนวทาง ข้ามแพลตฟอร์ม ในช่วงนี้ก็น่าสนใจ
      ด้วย interoperability ระหว่างภาษา ทำให้สามารถแชร์ไลบรารีเดียวกันบนหลายแพลตฟอร์มได้ในกรณีที่เหมาะสม
      ที่ผ่านมาทำกันด้วย C++ เท่านั้น และถ้าจำเป็นก็เสริม C API เข้าไป แต่ในสถานการณ์ที่ไม่ได้จำเป็นต้องใช้ C++ โดยตัวมันเอง แน่นอนว่ามันไม่ใช่ภาษาที่อยากเลือกใช้
      อย่างไรก็ตาม กังวลเรื่องต้นทุนอยู่ ถ้า Swift ในแอปมือถือเรียกไลบรารี Kotlin ได้ง่าย ๆ ก็รู้สึกว่าแอป iOS คงต้องโหลดอะไรสักอย่างที่คล้าย JVM ในรูปแบบหนึ่งหรือไม่ และในทางกลับกัน ถ้าแอป Android เรียก Swift ก็น่าจะต้องโหลด Swift runtime
      สุดท้ายก็จะมี overhead เกิดขึ้น
      กังวลว่าสักวันหนึ่งนักพัฒนาจะพึ่งพาไลบรารี Swift แล้วไลบรารีนั้นพึ่งพาไลบรารี Kotlin จนต้องเปิด JVM จากนั้นก็เรียก C++ ผ่าน JNI อีกที จนกลายเป็นเรื่องปกติ
      มันคล้ายกับสถานการณ์ของ package manager สมัยใหม่ที่ถึงแม้ dependency โดยตรงจะมีแค่ไม่กี่ตัว แต่เพราะ “มันง่ายเกินไปจนไม่ใส่ใจ” โปรแกรมจึงลงเอยด้วย transitive dependencies มากกว่า 100 รายการ ได้ในพริบตา
  • ดีใจที่ชุดบทความดี ๆ นี้ถูกนำมาแชร์ที่นี่ ได้เรียนรู้เกี่ยวกับ JVM เยอะมากจากซีรีส์นี้
    ชอบบทความนี้เป็นพิเศษ ซึ่งอธิบายว่าคำที่มักเรียกกันใน Java ว่า “stack allocation” นั้นไม่ถูกต้อง: https://shipilev.net/jvm/anatomy-quarks/18-scalar-replacemen...
    สิ่งที่ JVM ทำจริง ๆ คือ escape analysis + scalar replacement

  • ชอบ ความยาว ของบทความเหล่านี้
    สามารถอ่านจบทีละเรื่องได้ภายในไม่กี่นาที และถ้าต้องการก็ลองรัน benchmark ในเครื่องตัวเองได้ด้วย ดีมาก

  • ถ้าเคยทำงานกับภาษา JVM-based มาหลายปี ชุดบทความนี้น่าสนใจมากจริง ๆ
    ยังจำได้ว่าตอนอ่านไล่ไปครั้งแรกเมื่อหลายปีก่อนเป็นอย่างไร

  • มีใครรู้ไหมว่าทำไมชื่อซีรีส์นี้ถึงเปลี่ยนจาก ‘JVM Anatomy Park’?

    • ดูเหมือนจะเปลี่ยนชื่อช่วงที่เรื่องพฤติกรรมออนไลน์ของ Justin Rolland เริ่มเป็นที่รับรู้กัน
  • ผมแทบลืม Java ไปแล้ว
    ไม่ได้มีความคิดเลยว่าจะเริ่มโปรเจกต์ใหม่ด้วย Java
    ถ้าต้องการการพัฒนาที่รวดเร็วและความยืดหยุ่น ผมน่าจะเลือก Python; ถ้าอยากจัดการ I/O concurrency จำนวนมากด้วยแนวทางที่มี garbage collection ก็เลือก Go; ถ้าต้องการภาษาที่คอมไพล์และสมดุลดี ก็เลือก Swift; ถ้าต้องการภาษาแบบคอมไพล์ที่มีประสิทธิภาพและความปลอดภัย ก็เลือก Rust
    เป็นแค่รสนิยมส่วนตัว และรู้ว่า Kotlin ทำให้ Java ใช้ง่ายขึ้น แต่ก็ยังรู้สึกแบบนั้นอยู่ดี

    • คงไม่ได้มีแค่ผมที่คิดแบบนี้ แต่ก็น่าจะเป็นเพราะอยู่ในสถานการณ์ที่ไม่จำเป็นต้องใช้ Java
      จุดแข็งหลักของ Java คือมีนักพัฒนาที่มีประสบการณ์ Java มากแทบไม่จำกัด มีไลบรารีเดิมจำนวนมหาศาลและจำนวนมากก็เน้นฝั่ง enterprise จัดการ codebase ขนาดใหญ่มากที่มีผู้ร่วมพัฒนาจำนวนมากได้ง่าย VM มาตรฐาน ที่พัฒนามาหลายสิบปีมีความแข็งแกร่งมากและค่อนข้างเร็ว อีกทั้งรองรับแทบทุกแพลตฟอร์ม
      แม้จะไม่ได้ครองตลาดอย่างท่วมท้นเหมือนต้นทศวรรษ 2000 แม้แต่ใน enterprise และก็เป็นภาษา “blub” แบบคลาสสิก แต่ถ้าคาดหวัง ขนาดระดับ enterprise และการขยายทีมด้วยนักพัฒนาจำนวนมากสำคัญกว่าประสิทธิภาพล้วน ๆ ก็ยังเป็นตัวเลือกที่สมเหตุสมผลมาก
      ผมชอบ Rust แต่ Java เป็นตัวเลี้ยงปากท้องผม
    • ขอเสริมจากคำตอบอื่น Java ก็มีคุณสมบัติที่สตาร์ทอัพต้องการเพียงพอ จึงสมเหตุสมผลที่จะใช้กับโปรเจกต์ใหม่
      ด้วยเฟรมเวิร์กสมัยใหม่และตัวช่วย AI สามารถตั้ง backend ที่ใช้ได้ดีภายในไม่กี่วัน
      ถ้าเป็น co-founder ฝั่งเทคนิคคนเดียว แค่รู้ Java หรือ Kotlin และสแต็ก frontend ก็พอสำหรับทำ MVP ให้เร็ว และในความเป็นจริงจะใช้เวลากับงานนอกเหนือจากการเขียนโค้ดมากกว่ามาก ทำให้ความแตกต่างของฟีเจอร์ภาษาไม่สำคัญเท่าไร
      ถ้าไปทาง mobile native, Swift อาจเป็นภาษาที่สองได้
      อีกอย่าง ช่วงหนึ่ง scalability อาจยังไม่ใช่ปัญหาแรก ทีมจะต้องขยายก่อน และ performance bottleneck มักจะเห็นชัดทีหลังมาก
      Java เหมาะกับทีมใหญ่
      จากมุมมองธุรกิจ ถ้าต้องการ talent pool ที่ใหญ่กว่า รอบการส่งมอบที่รวดเร็ว และสิ่งที่อยู่เป็นสแต็กหลักได้ในระยะยาว Java หรือ Kotlin น่าจะเป็นตัวเลือกที่ดีที่สุด
      หากต้องการชูเทคโนโลยีเท่ ๆ เป็นเหมือนสวัสดิการเพื่อดึงดูดนักพัฒนาบางกลุ่ม หรือมีกรณีธุรกิจที่พบไม่บ่อย ก็อาจเลือก Go หรือ Rust
      Python เป็นที่นิยมในแวดวงวิชาการและ bootcamp แต่พูดตรง ๆ ผมไม่ค่อยแน่ใจเรื่องคุณค่าทางธุรกิจของมันสำหรับ backend ทั่วไป
    • บน JVM มีภาษาที่ใช้งานสะดวกมากมายอย่าง Clojure
      ผมคงไม่มองข้าม JVM ทั้งหมดหรอก JVM เป็น ผลงานวิศวกรรมชั้นเยี่ยม และทุกวันนี้ก็กำลังพัฒนาเร็ว ดู Loom, Panama, Leyden เป็นต้น
    • ถ้าจะตอบความเห็นที่ชวนดราม่าด้วยคำตอบที่ชวนดราม่าเหมือนกัน Java ดีกว่า Go ในทุกด้าน และ 90% ของแทบทุกกรณีที่คุณยกมา Java ก็รับมือได้
      ดังนั้นสำหรับแทบทุกอย่าง Java จึงเป็นตัวเลือกที่ดีค่อนข้างชัดเจน
    • คงไม่ได้มีแค่คุณที่คิดแบบนั้น แต่ก็ไม่ได้แปลว่าทุกคนจะคิดเหมือนกัน
      Kotlin ที่ใช้ร่วมกับ Java 21+ เป็นชุดที่ผมเลือกก่อนสำหรับบริการที่เน้น I/O หรือแทบจะบริการใด ๆ ก็ตาม
      มันใช้ง่ายจริง ๆ และด้วย virtual threads ก็ทำให้เขียนโค้ดได้เรียบง่ายและมีประสิทธิภาพเท่า Go ขณะเดียวกันก็ใช้ประโยชน์จาก ecosystem ไลบรารีที่ใหญ่และดีมากระดับโลกได้
      ไม่ได้จะด้อยค่า Go หรือ Python ถ้ามันเป็นเครื่องมือที่คุณชอบ ก็ถือว่าใช้ได้ดีพอ
      เพียงแต่ Java ไม่ได้หมดความสำคัญไปอย่างที่คุณคิด