1 điểm bởi GN⁺ 2024-11-12 | 1 bình luận | Chia sẻ qua WhatsApp
  • Đây là một loạt mini post đang tiếp diễn, chia nhỏ kiến thức nội bộ JVM thành các đơn vị nhỏ; mỗi bài tập trung vào một chủ đề, bài kiểm thử, benchmark hoặc quan sát
  • Mỗi bài hướng tới dung lượng đọc trong 5–10 phút, với giả định rằng các thành phần JVM tương tác với nhau
  • Bằng chứng và thảo luận có thể mang tính giai thoại, và có thể chưa được rà soát đầy đủ về lỗi, tính nhất quán, văn phong, ngữ pháp hay trùng lặp, nên cần thận trọng nếu tin tưởng nguyên văn
  • Toàn bộ bộ bài được cung cấp dưới dạng ePUB, MOBI, PDF; bản PDF có dung lượng ở mức hàng chục MB do chuyển đổi chất lượng cao
  • Chỉ mục các bài riêng lẻ được chia theo các trục Compiler, Runtime, GC, Library, bao gồm các chủ đề nội bộ JVM như tối ưu hóa khóa, TLAB, tạm dừng GC, String.intern(), safepoint, tham chiếu nén, conditional move

Những giả định khi đọc loạt bài

  • JVM Anatomy Quarks là một loạt mini post đang tiếp diễn, tóm tắt kiến thức nền tảng về JVM dưới dạng các bài viết ngắn
  • Mỗi bài đi sâu vào một chủ đề, bài kiểm thử, benchmark hoặc quan sát
  • Nếu tách riêng một bài đơn lẻ thì có thể thiếu ngữ cảnh, và phần lớn các yếu tố được bàn tới đều dễ tương tác với nhau
  • Bằng chứng và thảo luận trong bài có thể mang tính giai thoại, và có thể chưa được rà soát đầy đủ về lỗi, tính nhất quán, văn phong, ngữ pháp, ý nghĩa hay trùng lặp
  • Khi sử dụng hoặc tin tưởng nội dung, độc giả phải tự chịu rủi ro

Các file trọn bộ và cấu trúc chỉ mục

  • Toàn bộ loạt bài được cung cấp ở ba định dạng
    • ePUB là nhỏ nhất, dưới mức MB, dựa trên Pandoc HTML-to-ePUB
    • MOBI có dung lượng nhỏ, ở mức MB, dựa trên KindleGen ePUB-to-MOBI
    • PDF rất lớn, ở mức hàng chục MB, là đầu ra chất lượng cao dựa trên wkhtmltopdf HTML-to-PDF
  • Chỉ mục riêng lẻ được tổ chức theo các phân loại Compiler, Runtime, GC, Library

Danh sách bài viết theo chủ đề

  • Các mục trọng tâm 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
  • Các mục liên quan cả Runtime và GC

    • Đây là các bài bàn về bộ nhớ và hành vi tạm dừng trong khi JVM chạy
    • #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
  • Các mục trọng tâm GC

    • Chủ yếu bàn về thiết kế bộ thu gom và hành vi heap
    • #3: GC Design and Pauses
    • #11: Moving GC and Locality
    • #13: Intergenerational Barriers
    • #21: Heap Uncommit
  • Các mục trọng tâm Runtime

    • Đây là các bài bàn về môi trường thực thi JVM và biểu diễn đối tượng
    • #12: Native Memory Tracking
    • #23: Compressed References
    • #24: Object Alignment
    • #26: Identity Hash Code
  • Các mục Library hoặc phân loại kết hợp

    • Mục có bao gồm Library là #10: String.intern()
    • Các mục được đánh dấu đồng thời Compiler và Runtime là #16, #25, #29, #30

1 bình luận

 
GN⁺ 2024-11-12
Ý kiến trên Hacker News
  • https://shipilev.net/jvm/anatomy-quarks/17-trust-nonstatic-f... là một trường hợp thật sự đáng tiếc
    Vì một số framework đã lạm dụng JNI và reflection để thay đổi các trường final vốn dĩ phải bất biến, mã của người dùng đang bỏ lỡ những tối ưu hóa quan trọng vốn chỉ có thể áp dụng cho các lớp do hệ thống cung cấp
    Nền tảng, đặc biệt là compiler và runtime, cần thực thi các ràng buộc ngữ nghĩa thật nghiêm ngặt để bảo vệ dư địa tối ưu hóa trong tương lai

    • Chúng tôi đang thay đổi phần này như một phần của chiến lược integrity by default [1], và một JEP liên quan sẽ sớm xuất hiện
      Thực tế không có nhiều mã thật sự cần thay đổi final, và ngay cả hiện nay việc đó cũng bị giới hạn trong các lớp thuộc module của chính nó hoặc các lớp được open rõ ràng; vì vậy trong tương lai, ứng dụng sẽ phải cấp quyền cho module muốn thay đổi final
      Cách này tương tự những gì gần đây đã áp dụng cho native call và truy cập bộ nhớ không an toàn
      [1]: https://openjdk.org/jeps/8305968
    • Tôi tự hỏi liệu một số người có mù quáng làm theo Effective Java và tạo ra “tội tổ tông” kiểu hãy gắn final cho mọi thứ hay không
      Vì thế giờ đây trong test không thể dễ dàng mock các lớp final, và các công cụ mocking thậm chí phải thao tác bytecode để mock lớp final
      Ví dụ, bên trong Google, Effective Java là yêu cầu nên ngay cả API GDrive công khai cũng có lớp final, trong khi API bên ngoài mới chính là thứ người ta muốn mock
    • Trường hợp System.out là vấn đề của chính Java
      https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.htm...
      Với tiền lệ như vậy, không ngạc nhiên khi những người khác nghĩ đó là cách có thể chấp nhận được
    • Modifier của field là ràng buộc ngữ nghĩa, không phải ràng buộc bảo mật
      Đúng là phải có thể vượt qua chúng nếu đi qua quy trình né tránh phù hợp
      Điểm cốt lõi là an toàn, vì việc thay đổi thứ không thể sửa có thể gây SEGV, và đó chính là mối lo mà các access modifier muốn xử lý
    • Tôi thừa nhận mình từng tự viết ba dòng mã độc ác trong một thư viện nội bộ đã biến mất từ lâu: “un-final” một member, thay đổi nó, rồi lại làm cho nó trông như final, để tránh một đợt refactor khó chịu
      Giá mà bị chặn không làm được thì tốt, nhưng khi đó có yêu cầu kinh doanh
  • Hơi lạc đề một chút, nhưng Apple vừa đưa ra bridge Swift Java mới và nó khá hay
    Hỗ trợ cả JNI lẫn Panama, và tuần trước tôi đang port nó sang Android
    https://github.com/swiftlang/swift-java

    • Nếu có thể gọi là “hiện đại”, thì cách tiếp cận cross-platform ngày nay khá thú vị
      Nhờ khả năng tương tác giữa các ngôn ngữ, trong những trường hợp phù hợp có thể chia sẻ một thư viện trên nhiều nền tảng
      Cho đến nay việc này chủ yếu được làm bằng C++, kèm thêm C API nếu cần, nhưng trong những tình huống bản thân C++ không cần thiết thì hiển nhiên đó không phải ngôn ngữ được ưa thích
      Tuy vậy tôi lo về chi phí. Nếu Swift có thể dễ dàng gọi thư viện Kotlin trong ứng dụng di động, có lẽ ứng dụng iOS sẽ phải tải một thứ nào đó giống JVM dưới hình thức nào đó; ngược lại, nếu ứng dụng Android gọi Swift thì có vẻ nó sẽ tải Swift runtime
      Rốt cuộc sẽ phát sinh overhead
      Tôi lo rằng một ngày nào đó chuyện lập trình viên phụ thuộc vào thư viện Swift, thư viện đó lại phụ thuộc vào thư viện Kotlin làm khởi chạy JVM, rồi lại gọi C++ qua JNI, sẽ trở nên phổ biến
      Nó giống tình huống với các package manager hiện đại: dù phụ thuộc trực tiếp chỉ có vài cái, nhưng vì “quá dễ nên không để ý”, chương trình rất nhanh chóng có hơn 100 phụ thuộc bắc cầu
  • Rất vui khi chùm bài hay này được chia sẻ ở đây. Tôi đã học được rất nhiều về JVM từ series này
    Tôi đặc biệt thích bài này, nói rằng cách diễn đạt thường gọi là “cấp phát trên stack” trong Java là sai: https://shipilev.net/jvm/anatomy-quarks/18-scalar-replacemen...
    Điều JVM thực sự làm là escape analysis + scalar replacement

  • Tôi thích độ dài của các bài này
    Có thể đọc hết một bài trong vài phút, và nếu muốn thì chạy benchmark trên máy local cũng được, rất tiện

  • Nếu đã làm việc vài năm với các ngôn ngữ dựa trên JVM, tuyển tập bài này thật sự rất thú vị
    Tôi vẫn nhớ lần đầu đọc lần lượt các bài này vài năm trước

  • Có ai biết vì sao tên series này được đổi từ ‘JVM Anatomy Park’ không?

    • Có vẻ tên đã được đổi khi những chuyện về hành vi online của Justin Rolland bắt đầu được biết đến
  • Tôi gần như đã quên mất Java
    Tôi hoàn toàn không nghĩ đến việc bắt đầu một dự án mới bằng Java
    Nếu cần phát triển nhanh và linh hoạt thì tôi sẽ chọn Python; nếu muốn xử lý nhiều tác vụ I/O đồng thời theo cách có garbage collection thì chọn Go; nếu cần một ngôn ngữ được biên dịch, cân bằng và tốt thì chọn Swift; còn nếu cần ngôn ngữ biên dịch có hiệu năng và an toàn thì có lẽ tôi sẽ chọn Rust
    Đây chỉ là sở thích cá nhân, và tôi biết Kotlin đã khiến Java dễ dùng hơn, nhưng cảm giác của tôi vẫn là vậy

    • Chắc không chỉ riêng tôi như thế, nhưng có lẽ đó là tình huống không cần đến Java
      Các điểm mạnh chính của Java là số lượng lập trình viên có kinh nghiệm Java gần như vô hạn, kho thư viện hiện có rất đồ sộ và nhiều trong số đó hướng đến enterprise, dễ quản lý những codebase rất lớn với nhiều người đóng góp, còn VM tiêu chuẩn đã được phát triển qua nhiều thập kỷ thì cực kỳ vững chắc, khá nhanh và được hỗ trợ trên hầu như mọi nền tảng
      Nó không còn có vị thế thống trị áp đảo như đầu thập niên 2000, kể cả trong enterprise, và đúng là một ngôn ngữ “blub” điển hình; nhưng nếu dự kiến ở quy mô enterprise và việc mở rộng bằng cách có nhiều lập trình viên quan trọng hơn hiệu năng thuần túy, thì đây là một lựa chọn hoàn toàn hợp lý
      Tôi thích Rust, nhưng Java mới là thứ nuôi sống tôi
    • Bổ sung vào các câu trả lời khác, Java cũng có đủ những đặc tính mà startup cần, nên dùng nó cho dự án mới vẫn hợp lý
      Nhờ các framework hiện đại và hỗ trợ từ AI, bạn có thể dựng một backend ổn trong vài ngày
      Nếu là co-founder kỹ thuật một mình, để nhanh chóng tạo MVP thì chỉ cần biết Java hoặc Kotlin, cộng thêm stack frontend là đủ; thực tế là bạn sẽ dành nhiều thời gian hơn nhiều cho các việc ngoài coding, nên khác biệt về tính năng ngôn ngữ trở nên kém quan trọng hơn
      Nếu đi theo hướng mobile native, Swift có thể là ngôn ngữ thứ hai
      Ngoài ra, trong một thời gian, khả năng cao scalability chưa phải vấn đề đầu tiên. Việc mở rộng đội ngũ sẽ đến trước, còn nút thắt hiệu năng nhiều khả năng mãi sau mới thấy rõ
      Java phù hợp với các đội ngũ lớn
      Nhìn từ góc độ kinh doanh, nếu bạn muốn nguồn nhân lực lớn hơn, chu kỳ giao hàng nhanh và một thứ có thể tiếp tục là stack cốt lõi về dài hạn, thì Java hoặc Kotlin có lẽ là lựa chọn tốt nhất
      Nếu bạn muốn dùng công nghệ hào nhoáng như một dạng phúc lợi để thu hút một nhóm lập trình viên nhất định, hoặc có trường hợp kinh doanh hiếm gặp, bạn có thể chọn Go hoặc Rust
      Python phổ biến trong giới học thuật và bootcamp, nhưng nói thật tôi không rõ giá trị kinh doanh của nó với backend đa dụng
    • Trên JVM có nhiều ngôn ngữ rất dễ dùng như Clojure
      Tôi sẽ không phớt lờ toàn bộ JVM. JVM là một kiệt tác kỹ thuật và đang tiến hóa nhanh trong thời gian gần đây. Cứ nhìn Loom, Panama, Leyden là thấy
    • Đáp lại một bình luận dễ gây tranh cãi bằng một câu trả lời cũng dễ gây tranh cãi: Java tốt hơn Go ở mọi mặt, và 90% trong hầu hết các trường hợp bạn nêu đều có thể xử lý bằng Java
      Vì vậy, với gần như mọi thứ, Java là một lựa chọn tốt khá rõ ràng
    • Chắc không chỉ riêng bạn như vậy, nhưng cũng không phải ai cũng vậy
      Kotlin dùng cùng Java 21+ là tổ hợp tôi ưu tiên chọn, dù là dịch vụ thiên về I/O hay thực tế là bất kỳ dịch vụ nào
      Nó thực sự dễ dùng, và nhờ virtual thread, bạn có thể viết code đơn giản và hiệu quả như Go, đồng thời tận dụng một trong những hệ sinh thái thư viện lớn nhất và tốt nhất thế giới
      Tôi không có ý hạ thấp Go hay Python. Nếu đó là công cụ bạn thích thì hoàn toàn ổn
      Chỉ là Java chưa trở nên không còn liên quan như bạn nghĩ