Tại sao JVM cache JIT không biên dịch mã?


107

Việc triển khai JVM chuẩn từ Sun áp dụng một số tối ưu hóa khá phức tạp cho bytecode để có được tốc độ thực thi gần như nguyên bản sau khi mã được chạy một vài lần.

Câu hỏi đặt ra là, tại sao mã đã biên dịch này không được lưu trong bộ nhớ cache vào đĩa để sử dụng trong các lần sử dụng tiếp theo của cùng một hàm / lớp?

Như hiện tại, mỗi khi một chương trình được thực thi, trình biên dịch JIT khởi động lại, thay vì sử dụng phiên bản mã được biên dịch trước. Việc thêm tính năng này sẽ không tăng đáng kể thời gian chạy ban đầu của chương trình, khi mã bytecode về cơ bản đang được diễn giải?


4
Một chủ đề thảo luận về vấn đề này: javalobby.org/forums/thread.jspa?threadID=15812
miku

2
Nhưng một câu hỏi khó có thể thu hút một câu trả lời dứt khoát.
bmargulies

1
Tôi không chắc về mức tăng "đáng kể", vì khi đó bạn sẽ phải tải nội dung đã JITted từ đĩa thay vì JITing nó trong bộ nhớ. Nó có thể tăng tốc mọi thứ, nhưng trên cơ sở từng trường hợp.
R. Martinho Fernandes

1
Cảm ơn vì những câu trả lời tuyệt vời của tất cả mọi người! Tất cả các câu trả lời là giá trị ngang nhau, vì vậy tôi đã đi với cộng đồng về một này ...
Chinmay Kanchi

Đây là một câu hỏi hay nếu bạn hỏi tôi :)
Alfred

Câu trả lời:


25

Tôi nghi ngờ điều này là do các tối ưu hóa mà JVM thực hiện không phải là tĩnh, mà là động, dựa trên các mẫu dữ liệu cũng như các mẫu mã. Có khả năng các mẫu dữ liệu này sẽ thay đổi trong suốt thời gian tồn tại của ứng dụng, khiến các mức tối ưu được lưu trong bộ nhớ cache kém hơn mức tối ưu.

Vì vậy, bạn cần một cơ chế để xác định xem liệu các tối ưu hóa đã lưu có còn là tối ưu hay không, tại thời điểm đó, bạn có thể chỉ cần tối ưu hóa lại ngay lập tức.


6
... hoặc bạn có thể chỉ cung cấp tính bền bỉ như một tùy chọn , giống như JVM của Oracle - trao quyền cho các lập trình viên nâng cao để tối ưu hóa hiệu suất ứng dụng của họ khi nào và ở đâu họ chỉ cần biết các mẫu không thay đổi, thuộc trách nhiệm của họ. Tại sao không?!
Alex Martelli

2
Vì có lẽ nó không đáng. Nếu cả SUN, IBM và BEA đều không coi nó là đáng giá cho các JVM hiệu suất của họ, thì sẽ có lý do chính đáng cho điều đó. Có thể việc tối ưu hóa RT của họ nhanh hơn Oracle, đó là lý do tại sao Oracle lưu trữ nó.
skaffman

9
Tại sao không lấy các tính năng tối ưu đã lưu trữ làm điểm bắt đầu, để sử dụng những gì đã học được trong các lần chạy trước? Từ đó, JIT có thể hoạt động như bình thường để tối ưu hóa lại nội dung. Khi tắt, mã đó có thể được duy trì một lần nữa và được sử dụng trong lần chạy tiếp theo như một điểm bắt đầu mới.
Puce

1
@Puce Lý do duy nhất tôi có thể nghĩ đến là AFAIK bạn không nhận được số liệu thống kê hồ sơ khi chạy mã được tối ưu hóa. Vì vậy, bạn muốn không có cách nào để cải thiện ...
maaartinus

1
Cá nhân tôi sẽ ổn với tùy chọn "chỉ duy trì thông tin cấu hình JIT giữa các lần chạy" với tất cả các cảnh báo rằng "điều này sẽ chỉ hợp lệ với cùng một JVM, cùng một dữ liệu, v.v. và nếu không thì sẽ bị bỏ qua". Về lý do tại sao điều này không được thực hiện, tôi mong đợi rằng sự phức tạp thêm của việc duy trì và xác thực dữ liệu hạt giống JIT là quá nhiều để lấy tài nguyên từ các dự án khác. Với sự lựa chọn giữa luồng này và luồng lambda + của Java 8, tôi muốn có luồng sau.
Thorbjørn Ravn Andersen

25

JVM của Oracle thực sự được ghi nhận để làm như vậy - trích lời Oracle,

trình biên dịch có thể tận dụng mô hình phân giải lớp của Oracle JVM để tùy chọn duy trì các phương thức Java đã biên dịch trên các lệnh gọi, phiên hoặc phiên bản cơ sở dữ liệu. Sự bền bỉ như vậy tránh được chi phí biên dịch lại không cần thiết giữa các phiên hoặc phiên bản, khi biết rằng về mặt ngữ nghĩa mã Java không thay đổi.

Tôi không biết tại sao tất cả các triển khai VM phức tạp không cung cấp các tùy chọn tương tự.


8
Bởi vì JVM phức tạp khác không có một doanh nghiệp lớn còi RDBMS tiện dụng để lưu trữ nội dung trong :)
skaffman

Chà! điều đó có nghĩa là các Bộ sưu tập được lưu trong bộ nhớ cache. Đây là một tin tốt!
Sandeep Jindal

J9 của IBM cũng được ghi nhận là làm như vậy.
user314104

9
Lưu ý rằng Oracle JVM này là tệp bên trong Cơ sở dữ liệu Oracle, không phải tệp tải xuống mà Oracle nhận được khi mua Sun.
Thorbjørn Ravn Andersen,

14

Bản cập nhật cho các câu trả lời hiện có - Java 8 có một JEP dành riêng để giải quyết vấn đề này:

=> JEP 145: Mã biên dịch bộ nhớ cache . Liên kết mới .

Ở cấp độ rất cao, mục tiêu đã nêu của nó :

Lưu và sử dụng lại mã gốc đã biên dịch từ các lần chạy trước để cải thiện thời gian khởi động của các ứng dụng Java lớn.

Hi vọng điêu nay co ich.


3
Tính năng này vẫn chưa có trong bản phát hành cuối cùng .
assylias

5

Excelsior JET có trình biên dịch JIT bộ nhớ đệm kể từ phiên bản 2.0, được phát hành vào năm 2001. Hơn nữa, trình biên dịch AOT của nó có thể biên dịch lại bộ nhớ cache thành một DLL / đối tượng chia sẻ duy nhất bằng cách sử dụng tất cả các tối ưu hóa.


3
Có, nhưng câu hỏi là về JVM chuẩn, tức là JVM của Sun. Tôi biết rõ rằng có một số trình biên dịch AOT cho Java cũng như các JVM bộ nhớ đệm khác.
Chinmay Kanchi

0

Tôi không biết lý do thực sự, không liên quan đến việc thực hiện JVM, nhưng tôi có thể nghĩ ra một số lý do chính đáng:

  • Ý tưởng của Java là trở thành một ngôn ngữ viết-một-lần-chạy-ở bất cứ đâu và việc đưa những thứ được biên dịch sẵn vào tệp lớp là loại vi phạm điều đó (chỉ "loại" vì tất nhiên mã byte thực vẫn sẽ ở đó)
  • Nó sẽ làm tăng kích thước tệp lớp vì bạn sẽ có cùng một mã ở đó nhiều lần, đặc biệt nếu bạn tình cờ chạy cùng một chương trình dưới nhiều JVM khác nhau (điều này không thực sự phổ biến, khi bạn coi các phiên bản khác nhau là các JVM khác nhau, mà bạn thực sự phải làm)
  • Bản thân các tệp lớp có thể không ghi được (mặc dù sẽ khá dễ dàng để kiểm tra điều đó)
  • Các tối ưu hóa JVM một phần dựa trên thông tin về thời gian chạy và trên các lần chạy khác, chúng có thể không áp dụng được (mặc dù chúng vẫn sẽ mang lại một số lợi ích)

Nhưng tôi thực sự đang đoán, và như bạn có thể thấy, tôi không thực sự nghĩ rằng bất kỳ lý do nào trong số những lý do của tôi là thực sự dừng show. Tôi nghĩ Sun không coi hỗ trợ này là ưu tiên, và có lẽ lý do đầu tiên của tôi gần với sự thật, vì việc làm này theo thói quen cũng có thể khiến mọi người nghĩ rằng các tệp lớp Java thực sự cần một phiên bản riêng biệt cho mỗi máy ảo thay vì đa nền tảng.

Cách ưa thích của tôi thực sự sẽ là có một trình dịch bytecode-to-native riêng biệt mà bạn có thể sử dụng để làm điều gì đó như thế này một cách rõ ràng trước đó, tạo các tệp lớp được xây dựng rõ ràng cho một máy ảo cụ thể, có thể có mã byte gốc trong chúng để bạn cũng có thể chạy với các máy ảo khác nhau. Nhưng điều đó có lẽ xuất phát từ kinh nghiệm của tôi: Tôi hầu như chỉ làm Java ME, nơi mà thực sự đau đầu là trình biên dịch Java không thông minh hơn về biên dịch.


1
có một chỗ trong tệp lớp cho những thứ như vậy, nguyên nhân là mục đích ban đầu (lưu mã JIT'ed như một thuộc tính trong tệp lớp).
TofuBeer

@TofuBeer: Cảm ơn vì đã xác nhận. Tôi nghi ngờ đó có thể là trường hợp (đó là những gì tôi đã làm), nhưng không chắc chắn. Đã chỉnh sửa để loại bỏ đó là một lý do có thể.
JaakkoK

Tôi nghĩ bạn đã bắn trúng đầu với viên đạn cuối cùng của bạn. Những người khác có thể được làm việc xung quanh, nhưng phần cuối cùng, tôi nghĩ, lý do chính khiến mã JITed không được tồn tại.
Sasha Chedygov

1
Đoạn cuối cùng về trình biên dịch bytecode-to-native rõ ràng là những gì bạn hiện có trong .NET với NGEN ( msdn.microsoft.com/en-us/library/6t9t5wcf(VS.71).aspx ).
R. Martinho Fernandes
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.