Các quark giải phẫu JVM
(shipilev.net)- Đâ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
- Mục có bao gồm Library là
1 bình luận
Ý 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
finalvố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ấpNề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
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 đượcopenrõ ràng; vì vậy trong tương lai, ứng dụng sẽ phải cấp quyền cho module muốn thay đổifinalCá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
finalcho mọi thứ hay khôngVì 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ớpfinalVí 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 mockSystem.outlà vấn đề của chính Javahttps://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
Đú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ý
final, để tránh một đợt refactor khó chịuGiá 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
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?
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
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
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
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
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
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ĩ