Việc sử dụng chuyển đổi mã nguồn sang mã byte Java là gì?


37

Nếu một người cần các JVM khác nhau cho các kiến ​​trúc khác nhau, tôi không thể hiểu được logic đằng sau việc giới thiệu khái niệm này là gì. Trong các ngôn ngữ khác, chúng tôi cần các trình biên dịch khác nhau cho các máy khác nhau, nhưng trong Java, chúng tôi yêu cầu các JVM khác nhau, vậy logic đằng sau việc giới thiệu khái niệm về JVM hoặc bước bổ sung này là gì ??


1
Bản sao có thể có của Biên dịch thành mã byte so với mã máy
gnat

12
@gnat: Thật ra, đó không phải là một bản sao. Đây là "mã nguồn và mã byte", tức là chỉ chuyển đổi đầu tiên. Về ngôn ngữ, đây là Javascript so với Java; liên kết của bạn sẽ là C ++ so với Java.
MSalters

2
Bạn có muốn viết một trình thông dịch mã byte đơn giản cho 50 kiểu thiết bị mà bạn đang thêm mã hóa kỹ thuật số để nâng cấp hoặc 50 trình biên dịch cho 50 phần cứng khác nhau. Java ban đầu được phát triển cho các thiết bị và máy móc. Đó là bộ đồ mạnh mẽ của nó. Hãy ghi nhớ điều đó khi đọc những câu trả lời này vì java không có lợi thế thực sự hiện nay (do không hiệu quả của quá trình phiên dịch). Nó chỉ là một mô hình chúng tôi tiếp tục sử dụng.
Vịt lớn

1
Bạn dường như không hiểu máy ảo là gì . Đó là một cái máy. Nó có thể được triển khai trong phần cứng với các trình biên dịch mã gốc (và trong trường hợp JVM, nó đã được). Phần 'ảo' là điều quan trọng ở đây: về cơ bản bạn đang mô phỏng kiến trúc đó trên đỉnh của một kiến ​​trúc khác. Nói rằng tôi đã viết một trình giả lập 8088 để chạy trên x86. Bạn sẽ không chuyển mã 8088 cũ sang x86, bạn sẽ chạy nó trên nền tảng giả lập. JVM là một máy bạn nhắm mục tiêu như bất kỳ máy nào khác, sự khác biệt là nó chạy trên các nền tảng khác.
Jared Smith

7
@TheGreatDuck Quá trình phiên dịch? Hầu hết các JVM hiện nay đều thực hiện biên dịch đúng lúc cho mã máy. Chưa kể rằng "giải thích" là một thuật ngữ khá rộng hiện nay. Bản thân CPU chỉ "diễn giải" mã x86 thành vi mã bên trong của chính nó và nó được sử dụng để cải thiện hiệu quả. Các CPU Intel mới nhất cũng cực kỳ phù hợp với các phiên dịch viên nói chung (mặc dù tất nhiên bạn sẽ tìm thấy điểm chuẩn để chứng minh bất cứ điều gì bạn muốn chứng minh).
Luaan

Câu trả lời:


79

Logic là mã byte JVM đơn giản hơn mã nguồn Java rất nhiều.

Trình biên dịch có thể được coi là, ở mức độ trừu tượng cao, có ba phần cơ bản: phân tích cú pháp, phân tích ngữ nghĩa và tạo mã.

Phân tích cú pháp bao gồm đọc mã và biến nó thành biểu diễn cây bên trong bộ nhớ của trình biên dịch. Phân tích ngữ nghĩa là phần mà nó phân tích cây này, tìm hiểu ý nghĩa của nó và đơn giản hóa tất cả các cấu trúc cấp cao xuống các cấp thấp hơn. Và việc tạo mã lấy cây đơn giản hóa và viết nó thành một đầu ra phẳng.

Với tệp mã byte, giai đoạn phân tích cú pháp được đơn giản hóa rất nhiều, vì nó được viết theo cùng một định dạng luồng byte phẳng mà JIT sử dụng, thay vì ngôn ngữ nguồn đệ quy (cấu trúc cây). Ngoài ra, rất nhiều sự phân tích ngữ nghĩa nặng nề đã được trình biên dịch Java (hoặc ngôn ngữ khác) thực hiện. Vì vậy, tất cả những gì phải làm là đọc luồng mã, phân tích cú pháp tối thiểu và phân tích ngữ nghĩa tối thiểu và sau đó thực hiện tạo mã.

Điều này làm cho nhiệm vụ mà JIT phải thực hiện đơn giản hơn rất nhiều và do đó thực thi nhanh hơn rất nhiều, trong khi vẫn bảo tồn siêu dữ liệu cấp cao và thông tin ngữ nghĩa giúp cho về mặt lý thuyết có thể viết mã nguồn đơn, đa nền tảng.


7
Một số nỗ lực ban đầu khác trong phân phối applet, như SafeTCL, đã thực sự phân phối mã nguồn. Việc Java sử dụng một mã byte đơn giản và được chỉ định chặt chẽ làm cho việc xác minh chương trình trở nên dễ hiểu hơn nhiều và đó là vấn đề khó giải quyết. Các mã byte như mã p đã được biết đến như là một phần của giải pháp cho vấn đề tính di động (và ANDF có lẽ đang được phát triển vào thời điểm đó).
Toby Speight

9
Đúng. Thời gian khởi động Java đã có một chút vấn đề do bước mã byte -> mã máy. Chạy javac trên dự án (không tầm thường) của bạn và sau đó tưởng tượng thực hiện toàn bộ mã Java -> mã máy đó mỗi khi khởi động.
Paul Draper

24
Nó có một lợi ích to lớn khác: nếu một ngày nào đó tất cả chúng ta muốn chuyển sang một ngôn ngữ mới giả định - hãy gọi nó là "Scala" - chúng ta chỉ cần viết một Scala -> trình biên dịch mã byte, thay vì hàng tá Scala -> mã máy trình biên dịch. Như một phần thưởng, chúng tôi nhận được tất cả các tối ưu hóa dành riêng cho nền tảng của JVM.
BlueRaja - Daniel Pflughoeft

8
Một số điều vẫn không thể thực hiện được trong mã byte JVM, chẳng hạn như tối ưu hóa cuộc gọi đuôi. Tôi nhớ điều này làm ảnh hưởng rất lớn đến một ngôn ngữ chức năng biên dịch thành JVM.
JDługosz

8
@ JDługosz phải: JVM không may áp đặt khá nhiều hạn chế / thành ngữ thiết kế, trong khi chúng có thể hoàn toàn tự nhiên nếu bạn đến từ một ngôn ngữ bắt buộc, có thể trở thành một trở ngại khá giả tạo nếu bạn muốn viết một trình biên dịch cho một ngôn ngữ hoạt động cơ bản khác nhau. Do đó, tôi coi LLVM là mục tiêu tốt hơn, liên quan đến việc tái sử dụng ngôn ngữ trong tương lai - nó cũng có những hạn chế, nhưng chúng ít nhiều phù hợp với những hạn chế mà các bộ xử lý hiện tại (và có thể trong tương lai) có.
leftaroundabout

27

Các đại diện trung gian của các loại khác nhau đang ngày càng phổ biến trong thiết kế trình biên dịch / thời gian chạy, vì một vài lý do.

Trong trường hợp của Java, lý do số một ban đầu có lẽ là tính di động : Java ban đầu được quảng cáo rầm rộ là "Viết một lần, chạy mọi nơi". Mặc dù bạn có thể đạt được điều này bằng cách phân phối mã nguồn và sử dụng các trình biên dịch khác nhau để nhắm mục tiêu các nền tảng khác nhau, nhưng điều này có một vài nhược điểm:

  • trình biên dịch là các công cụ phức tạp phải hiểu tất cả các cú pháp tiện lợi của ngôn ngữ; mã byte có thể là một ngôn ngữ đơn giản hơn, vì nó gần với mã thực thi của máy hơn là nguồn có thể đọc được của con người; điều này có nghĩa là:
    • quá trình biên dịch có thể chậm so với thực thi mã byte
    • trình biên dịch nhắm mục tiêu các nền tảng khác nhau có thể sẽ tạo ra hành vi khác nhau hoặc không theo kịp sự thay đổi ngôn ngữ
    • việc tạo một trình biên dịch cho một nền tảng mới khó hơn rất nhiều so với việc tạo ra một trình biên dịch VM (hoặc trình biên dịch mã gốc-by -ode) cho nền tảng đó
  • phân phối mã nguồn không phải lúc nào cũng mong muốn; bytecode cung cấp một số bảo vệ chống lại kỹ thuật đảo ngược (mặc dù nó vẫn khá dễ dịch ngược trừ khi cố tình làm xáo trộn)

Các ưu điểm khác của một đại diện trung gian bao gồm:

  • tối ưu hóa , trong đó các mẫu có thể được phát hiện trong mã byte và được biên dịch thành tương đương nhanh hơn hoặc thậm chí được tối ưu hóa cho các trường hợp đặc biệt khi chương trình chạy (sử dụng trình biên dịch "JIT" hoặc "Just in Time")
  • khả năng tương tác giữa nhiều ngôn ngữ trong cùng một VM; điều này đã trở nên phổ biến với JVM (ví dụ Scala) và là mục đích rõ ràng của khung .net

1
Java cũng được định hướng cho các hệ thống nhúng. Trong các hệ thống như vậy, phần cứng có một số hạn chế về bộ nhớ và cpu.
Laiv

Các trình biên dịch có thể được phát triển theo cách đầu tiên chúng biên dịch mã nguồn Java thành mã byte và sau đó biên dịch mã byte thành mã máy không? Nó sẽ loại bỏ hầu hết các nhược điểm bạn đề cập?
Sher10ck

@ Sher10ck Có, AFAIK hoàn toàn có thể viết trình biên dịch chuyển đổi tĩnh mã byte JVM thành hướng dẫn máy cho một kiến ​​trúc cụ thể. Nhưng nó sẽ chỉ có ý nghĩa nếu nó cải thiện hiệu suất đủ lớn hơn cả nỗ lực thêm cho nhà phân phối, hoặc thêm thời gian để sử dụng lần đầu cho người dùng. Một hệ thống nhúng công suất thấp có thể có lợi; một PC hiện đại tải xuống và chạy nhiều chương trình khác nhau có lẽ sẽ tốt hơn với JIT được điều chỉnh tốt. Tôi nghĩ Android đi đâu đó theo hướng này, nhưng không biết chi tiết.
IMSoP

8

Có vẻ như bạn đang tự hỏi tại sao chúng ta không phân phối mã nguồn. Hãy để tôi chuyển câu hỏi đó: tại sao chúng ta không phân phối mã máy?

Rõ ràng câu trả lời ở đây là Java, theo thiết kế, không cho rằng nó biết máy là nơi mã của bạn sẽ chạy; nó có thể là máy tính để bàn, siêu máy tính, điện thoại hoặc bất cứ thứ gì ở giữa và xa hơn. Java chừa chỗ cho trình biên dịch JVM cục bộ thực hiện công việc của nó. Ngoài việc tăng tính di động của mã của bạn, điều này còn có lợi ích tốt khi cho phép trình biên dịch thực hiện những việc như tận dụng tối ưu hóa cụ thể của máy, nếu chúng tồn tại hoặc vẫn tạo ra ít nhất mã hoạt động nếu chúng không hoạt động. Những thứ như hướng dẫn SSE hoặc tăng tốc phần cứng chỉ có thể được sử dụng trên các máy hỗ trợ chúng.

Nhìn thấy trong ánh sáng này, lý do sử dụng mã byte trên mã nguồn thô là rõ ràng hơn. Càng gần với ngôn ngữ máy thô càng tốt cho phép chúng tôi nhận ra hoặc nhận ra một phần lợi ích của mã máy, chẳng hạn như:

  • Thời gian khởi động nhanh hơn, vì một số quá trình biên dịch và phân tích đã được thực hiện.
  • Bảo mật, vì định dạng mã byte có cơ chế tích hợp để ký các tệp phân phối (nguồn có thể thực hiện điều này theo quy ước, nhưng cơ chế để thực hiện điều này không được tích hợp theo cách của mã byte).

Lưu ý rằng tôi không đề cập đến việc thực hiện nhanh hơn. Cả mã nguồn và mã byte đều hoặc có thể (về lý thuyết) được biên dịch đầy đủ vào cùng một mã máy để thực thi thực tế.

Ngoài ra, mã byte cho phép một số cải tiến so với mã máy. Tất nhiên có sự độc lập nền tảng và tối ưu hóa phần cứng cụ thể mà tôi đã đề cập trước đó, nhưng cũng có những thứ như phục vụ trình biên dịch JVM để tạo các đường dẫn thực thi mới từ mã cũ. Điều này có thể là để vá các vấn đề bảo mật hoặc nếu phát hiện tối ưu hóa mới hoặc tận dụng các hướng dẫn phần cứng mới. Trong thực tế, hiếm khi thấy những thay đổi lớn theo cách này, bởi vì nó có thể phơi bày các lỗi, nhưng điều đó là có thể, và đó là điều xảy ra theo những cách nhỏ mọi lúc.


8

Dường như có ít nhất hai câu hỏi khác nhau có thể có ở đây. Một là thực sự về trình biên dịch nói chung, với Java về cơ bản chỉ là một ví dụ về thể loại này. Cái khác là cụ thể hơn đối với Java các mã byte cụ thể mà nó sử dụng.

Trình biên dịch nói chung

Trước tiên chúng ta hãy xem xét câu hỏi chung: tại sao trình biên dịch sẽ sử dụng một số biểu diễn trung gian trong quá trình biên dịch mã nguồn để chạy trên một số bộ xử lý cụ thể?

Giảm độ phức tạp

Một câu trả lời khá đơn giản: nó chuyển đổi một vấn đề O (N * M) thành vấn đề O (N + M).

Nếu chúng tôi cung cấp các ngôn ngữ nguồn N và các mục tiêu M và mỗi trình biên dịch hoàn toàn độc lập, thì chúng tôi cần trình biên dịch N * M để dịch tất cả các ngôn ngữ nguồn đó sang tất cả các mục tiêu đó (trong đó "mục tiêu" là một sự kết hợp của một bộ xử lý và HĐH).

Tuy nhiên, nếu tất cả các trình biên dịch đồng ý về một biểu diễn trung gian chung, thì chúng ta có thể có các giao diện N của trình biên dịch dịch các ngôn ngữ nguồn sang biểu diễn trung gian và các trình biên dịch M kết thúc dịch đại diện trung gian sang một mục tiêu phù hợp cho một mục tiêu cụ thể.

Phân đoạn vấn đề

Vẫn tốt hơn, nó phân tách vấn đề thành hai hoặc nhiều miền độc quyền. Những người biết / quan tâm đến thiết kế ngôn ngữ, phân tích cú pháp và những thứ như thế có thể tập trung vào giao diện trình biên dịch, trong khi những người biết về tập lệnh, thiết kế bộ xử lý và những thứ như thế có thể tập trung vào mặt sau.

Vì vậy, ví dụ, được cung cấp một cái gì đó như LLVM, chúng tôi có rất nhiều giao diện cho các ngôn ngữ khác nhau. Chúng tôi cũng có back-end cho rất nhiều bộ xử lý khác nhau. Một anh chàng ngôn ngữ có thể viết một giao diện mới cho ngôn ngữ của mình và nhanh chóng hỗ trợ rất nhiều mục tiêu. Một anh chàng bộ xử lý có thể viết một back-end mới cho mục tiêu của mình mà không phải xử lý thiết kế ngôn ngữ, phân tích cú pháp, v.v.

Tách các trình biên dịch thành một mặt trước và mặt sau, với một biểu diễn trung gian để giao tiếp giữa hai trình biên dịch không phải là bản gốc với Java. Đó là cách làm khá phổ biến trong một thời gian dài (dù sao trước khi Java xuất hiện).

Mô hình phân phối

Trong phạm vi mà Java đã thêm bất cứ điều gì mới về mặt này, thì đó là trong mô hình phân phối. Đặc biệt, mặc dù các trình biên dịch đã được tách thành các phần đầu và cuối trong nội bộ trong một thời gian dài, chúng thường được phân phối dưới dạng một sản phẩm. Ví dụ: nếu bạn đã mua trình biên dịch Microsoft C, bên trong nó có "C1" và "C2", tương ứng là mặt trước và mặt sau - nhưng thứ bạn mua chỉ là "Microsoft C" bao gồm cả hai các phần (với một "trình điều khiển trình biên dịch" phối hợp các hoạt động giữa hai phần). Mặc dù trình biên dịch được xây dựng thành hai phần, nhưng đối với một nhà phát triển bình thường sử dụng trình biên dịch thì đó chỉ là một thứ duy nhất được dịch từ mã nguồn sang mã đối tượng, không có gì hiển thị ở giữa.

Thay vào đó, Java đã phân phối front-end trong Bộ công cụ phát triển Java và back-end trong Máy ảo Java. Mọi người dùng Java đều có trình biên dịch back-end để nhắm mục tiêu bất kỳ hệ thống nào anh ta đang sử dụng. Các nhà phát triển Java đã phân phối mã ở định dạng trung gian, vì vậy khi người dùng tải nó, JVM đã làm bất cứ điều gì cần thiết để thực thi nó trên máy cụ thể của họ.

Tiền lệ

Lưu ý rằng mô hình phân phối này cũng không hoàn toàn mới. Ví dụ, hệ thống P của UCSD hoạt động tương tự: mặt trước của trình biên dịch tạo ra mã P và mỗi bản sao của hệ thống P bao gồm một máy ảo thực hiện những gì cần thiết để thực thi mã P trên mục tiêu cụ thể 1 đó .

Mã byte Java

Mã byte Java khá giống với mã P. Đó là hướng dẫn cơ bản cho một máy khá đơn giản. Máy đó được dự định là một bản tóm tắt của các máy hiện có, do đó khá dễ dàng để dịch nhanh chóng sang hầu hết mọi mục tiêu cụ thể. Dễ dịch là từ rất quan trọng vì mục đích ban đầu là giải thích mã byte, giống như P-System đã thực hiện (và, vâng, đó chính xác là cách các triển khai ban đầu hoạt động).

Điểm mạnh

Mã byte Java dễ dàng cho một giao diện người biên dịch sản xuất. Nếu (ví dụ) bạn có một cây khá điển hình đại diện cho một biểu thức, nó thường khá dễ dàng để duyệt qua cây và tạo mã khá trực tiếp từ những gì bạn tìm thấy ở mỗi nút.

Mã byte Java khá nhỏ gọn - trong hầu hết các trường hợp, nhỏ gọn hơn nhiều so với mã nguồn hoặc mã máy cho hầu hết các bộ xử lý điển hình (và, đặc biệt đối với hầu hết các bộ xử lý RISC, như SPARC mà Sun bán khi họ thiết kế Java). Điều này đặc biệt quan trọng vào thời điểm đó, bởi vì một mục đích chính của Java là hỗ trợ các applet - mã được nhúng trong các trang web sẽ được tải xuống trước khi thực thi - tại thời điểm hầu hết mọi người truy cập chúng tôi qua modem qua các đường dây điện thoại vào khoảng 28.8 kilobit mỗi giây (mặc dù, tất nhiên, vẫn còn khá nhiều người sử dụng modem cũ hơn, chậm hơn).

Những điểm yếu

Điểm yếu lớn của mã byte Java là chúng không đặc biệt biểu cảm. Mặc dù chúng có thể diễn đạt các khái niệm hiện diện trong Java khá tốt, nhưng chúng không hoạt động gần như tốt để thể hiện các khái niệm không phải là một phần của Java. Tương tự như vậy, trong khi thật dễ dàng để thực thi mã byte trên hầu hết các máy, thì điều đó lại khó hơn nhiều theo cách tận dụng tối đa bất kỳ máy cụ thể nào.

Ví dụ, một thói quen khá thú vị là nếu bạn thực sự muốn tối ưu hóa mã byte Java, về cơ bản, bạn thực hiện một số kỹ thuật đảo ngược để dịch ngược chúng từ biểu diễn giống như mã máy và biến chúng trở lại thành các lệnh SSA (hoặc một cái gì đó tương tự) 2 . Sau đó, bạn thao tác các hướng dẫn SSA để thực hiện tối ưu hóa, sau đó dịch từ đó sang thứ gì đó nhắm vào kiến ​​trúc mà bạn thực sự quan tâm. Tuy nhiên, ngay cả với quy trình khá phức tạp này, một số khái niệm xa lạ với Java cũng khó diễn đạt đến mức khó dịch từ một số ngôn ngữ nguồn sang mã máy chạy (thậm chí gần) tối ưu trên hầu hết các máy điển hình.

Tóm lược

Nếu bạn đang hỏi về lý do sử dụng các biểu diễn trung gian nói chung, hai yếu tố chính là:

  1. Giảm vấn đề O (N * M) thành vấn đề O (N + M) và
  2. Phá vỡ vấn đề thành nhiều phần dễ quản lý hơn.

Nếu bạn đang hỏi về các chi tiết cụ thể của mã byte Java và lý do tại sao họ chọn đại diện cụ thể này thay vì một số khác, thì tôi sẽ nói rằng câu trả lời chủ yếu trở lại với mục đích ban đầu của họ và các hạn chế của web tại thời điểm đó , dẫn đến các ưu tiên sau:

  1. Đại diện nhỏ gọn.
  2. Nhanh chóng và dễ dàng để giải mã và thực hiện.
  3. Nhanh chóng và dễ dàng để thực hiện trên hầu hết các máy phổ biến.

Có thể đại diện cho nhiều ngôn ngữ hoặc thực hiện tối ưu trên nhiều mục tiêu khác nhau là các ưu tiên thấp hơn nhiều (nếu chúng được coi là ưu tiên).


  1. Vậy tại sao hệ thống P chủ yếu bị lãng quên? Chủ yếu là một tình huống giá cả. Hệ thống P được bán khá nhiều trên Apple II, Commodore Super, v.v. Khi PC của IBM ra mắt, hệ thống P là một hệ điều hành được hỗ trợ, nhưng MS-DOS có giá thấp hơn (theo quan điểm của hầu hết mọi người, về cơ bản được ném vào miễn phí) và nhanh chóng có sẵn nhiều chương trình hơn, vì đó là những gì Microsoft và IBM (trong số những người khác) đã viết cho.
  2. Ví dụ, đây là cách Soot hoạt động.

Khá gần với các applet web: mục đích ban đầu là phân phối mã cho các thiết bị (đặt các hộp trên cùng ...), giống như cách RPC phân phối các lệnh gọi hàm và CORBA phân phối các đối tượng.
ninjalj

2
Đây là một câu trả lời tuyệt vời, và một cái nhìn sâu sắc tốt về cách các đại diện trung gian khác nhau tạo ra sự đánh đổi khác nhau. :)
IMSoP

@ninjalj: Đó thực sự là Oak. Vào thời điểm nó biến thành Java, tôi tin rằng các ý tưởng hộp hàng đầu (và tương tự) đã bị gác lại (mặc dù tôi là người đầu tiên thừa nhận rằng có một lập luận công bằng để đưa ra rằng Oak và Java là cùng một thứ).
Jerry Coffin

@TobySpeight: Vâng, biểu hiện có lẽ phù hợp hơn ở đó. Cảm ơn.
Jerry Coffin

0

Ngoài những lợi thế mà người khác đã chỉ ra, bytecode nhỏ hơn rất nhiều, do đó việc phân phối và cập nhật dễ dàng hơn và chiếm ít không gian hơn trong môi trường mục tiêu. Điều này đặc biệt quan trọng trong môi trường bị giới hạn không gian nhiều.

Nó cũng làm cho nó dễ dàng hơn để bảo vệ mã nguồn có bản quyền.


2
Mã byte của Java (và .NET) rất dễ biến trở lại thành nguồn dễ đọc hợp lý đến mức có những sản phẩm để mang tên và đôi khi thông tin khác để làm cho điều này khó hơn nữa. Một cái gì đó thường được thực hiện với JavaScript để làm cho nó nhỏ hơn có thể thiết lập trên mã byte cho trình duyệt Web.
LnxPrgr3

0

Ý nghĩa là việc biên dịch từ mã byte sang mã máy nhanh hơn so với việc diễn giải mã gốc của bạn thành mã máy đúng lúc. Nhưng chúng tôi cần diễn giải để làm cho ứng dụng của chúng tôi đa nền tảng, bởi vì chúng tôi muốn sử dụng mã gốc của chúng tôi trên mọi nền tảng mà không cần thay đổi và không có bất kỳ sự chuẩn bị nào (biên dịch). Vì vậy, javac đầu tiên biên dịch mã nguồn của chúng tôi thành mã byte, sau đó chúng tôi có thể chạy mã byte này ở bất cứ đâu và nó sẽ được Máy ảo Java giải thích thành mã máy nhanh hơn. Câu trả lời: nó tiết kiệm thời gian.


0

Ban đầu, JVM là một trình thông dịch thuần túy . Và bạn có được trình thông dịch hoạt động tốt nhất nếu ngôn ngữ mà bạn đang phiên dịch càng đơn giản càng tốt. Đó là mục tiêu của mã byte: Để cung cấp đầu vào có thể hiểu một cách hiệu quả cho môi trường thời gian chạy. Quyết định duy nhất này đặt Java gần với một ngôn ngữ được biên dịch hơn là một ngôn ngữ được giải thích, như được đánh giá bởi hiệu suất của nó.

Chỉ sau này, khi rõ ràng hiệu năng của các JVM phiên dịch vẫn còn bị thu hút, mọi người đã đầu tư công sức để tạo ra các trình biên dịch đúng lúc hoạt động tốt. Điều này phần nào thu hẹp khoảng cách với các ngôn ngữ nhanh hơn như C và C ++. (Tuy nhiên, vẫn còn một số vấn đề về tốc độ vốn có của Java, do đó bạn có thể sẽ không bao giờ có được môi trường Java thực hiện tốt như mã C được viết.)

Tất nhiên, với các kỹ thuật biên dịch đúng lúc, chúng ta có thể quay lại phân phối mã nguồn thực sự và biên dịch mã kịp thời thành mã máy. Tuy nhiên, điều này sẽ làm giảm đáng kể hiệu năng khởi động cho đến khi tất cả các phần có liên quan của mã được biên dịch. Mã byte vẫn là một trợ giúp đáng kể ở đây vì nó đơn giản hơn nhiều so với mã Java tương đương.


Downvoter xin vui lòng giải thích lý do tại sao ?
cmaster

-5

Mã nguồn văn bản là một cấu trúc có ý định dễ đọc và sửa đổi bởi một con người.

Mã byte là một cấu trúc có ý định dễ đọc và được thực thi bởi một máy.

Vì tất cả các JVM làm với mã được đọc và thực thi nó, mã byte phù hợp hơn để tiêu thụ bởi JVM.

Tôi nhận thấy rằng chưa có ví dụ nào. Ví dụ giả ngớ ngẩn:

//Source code
i += 1 + 5 * 2 + x;

// Byte code
i += 11, i += x
____

//Source code
i = sin(1);

// Byte code
i = 0.8414709848
_____

//Source code
i = sin(x)^2+cos(x)^2;

// Byte code (actually that one isn't true)
i = 1

Tất nhiên mã byte không chỉ là về tối ưu hóa. Một phần lớn của nó là về khả năng thực thi mã mà không cần phải quan tâm đến các quy tắc phức tạp, như kiểm tra xem lớp có chứa một thành viên được gọi là "foo" ở đâu đó trong tệp khi phương thức đề cập đến "foo" hay không.


2
Những "ví dụ" mã byte có thể đọc được. Đó hoàn toàn không phải là mã byte. Điều này là sai lệch và cũng không giải quyết câu hỏi được hỏi.
tự đại diện

@Wildcard Bạn có thể đã bỏ lỡ rằng đây là một diễn đàn, được đọc bởi con người. Đó là lý do tại sao tôi đặt nội dung ở dạng người có thể đọc được. Cho rằng diễn đàn là về công nghệ phần mềm, yêu cầu người đọc hiểu khái niệm trừu tượng đơn giản là không yêu cầu nhiều.
Peter

Dạng người có thể đọc được là mã nguồn, không phải mã byte. Bạn đang minh họa mã nguồn bằng các biểu thức được tính toán trước, KHÔNG phải mã byte. Và tôi đã không bỏ lỡ rằng đây là một diễn đàn dễ đọc với con người: Bạn là người chỉ trích những người trả lời khác vì không bao gồm bất kỳ ví dụ nào về mã byte, không phải tôi. Vì vậy, bạn nói, "Tôi nhận thấy chưa có bất kỳ ví dụ nào", và sau đó tiến hành đưa ra các mẫu không phải là không minh họa mã byte nào cả. Và điều này vẫn không giải quyết câu hỏi nào cả. Đọc lại câu hỏi.
Wildcard
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.