Hãy bắt đầu với hai lớp đơn giản:
package com.michaelt.so.supers;
public class Sup {
int methodA(int a, int b) {
return a + b;
}
}
và sau đó
package com.michaelt.so.supers;
public class Sub extends Sup {
@Override
int methodA(int a, int b) {
return super.methodA(a, b);
}
}
Phương thức biên dịchA và nhìn vào mã byte người ta nhận được:
methodA(II)I
L0
LINENUMBER 6 L0
ALOAD 0
ILOAD 1
ILOAD 2
INVOKESPECIAL com/michaelt/so/supers/Sup.methodA (II)I
IRETURN
L1
LOCALVARIABLE this Lcom/michaelt/so/supers/Sub; L0 L1 0
LOCALVARIABLE a I L0 L1 1
LOCALVARIABLE b I L0 L1 2
MAXSTACK = 3
MAXLOCALS = 3
Và bạn có thể thấy ngay ở đó với phương thức invokespecial nó thực hiện tra cứu theo phương thức lớp SupA ().
Các invokespecial opcode có logic sau đây:
- Nếu C chứa một khai báo cho một phương thức cá thể có cùng tên và mô tả như phương thức được giải quyết, thì phương thức này sẽ được gọi. Thủ tục tra cứu chấm dứt.
- Mặt khác, nếu C có siêu lớp, quy trình tra cứu tương tự này được thực hiện đệ quy bằng cách sử dụng siêu lớp trực tiếp của C. Phương thức được gọi là kết quả của việc gọi đệ quy của quy trình tra cứu này.
- Mặt khác, một AbstractMethodError được đưa ra.
Trong trường hợp này, không có phương thức cá thể nào có cùng tên và mô tả trong lớp của anh ta nên viên đạn đầu tiên sẽ không bắn. Tuy nhiên, viên đạn thứ hai sẽ - có một siêu lớp và nó gọi phương thức của siêu nhân.
Trình biên dịch không nội tuyến này và không có bản sao nguồn Sup trong lớp.
Tuy nhiên câu chuyện vẫn chưa kết thúc. Đây chỉ làmã được biên dịch . Khi mã đạt JVM, HotSpot có thể tham gia.
Thật không may, tôi không biết nhiều về nó, vì vậy tôi sẽ khiếu nại chính quyền về vấn đề này và đi đến Inlining trong Java nơi người ta nói rằng HotSpot có thể phương thức nội tuyến (ngay cả phương thức không phải là cuối cùng).
Đi đến các tài liệu cần lưu ý rằng nếu một lệnh gọi phương thức cụ thể trở thành một điểm nóng thay vì thực hiện tra cứu đó mỗi lần, thông tin này có thể được nội tuyến - sao chép mã hiệu quả từ Sup methodA () vào Sub methodA ().
Điều này được thực hiện trong thời gian chạy, trong bộ nhớ, dựa trên cách ứng dụng hoạt động và những tối ưu hóa nào là cần thiết để tăng tốc hiệu suất.
Như đã nêu trong HotSpot Internals cho OpenJDK "Các phương thức thường được nội tuyến. Các cách gọi tĩnh, riêng tư, cuối cùng và / hoặc" đặc biệt "rất dễ để nội tuyến."
Nếu bạn đào sâu vào các tùy chọn cho JVM, bạn sẽ tìm thấy một tùy chọn -XX:MaxInlineSize=35
(35 là mặc định) là số byte tối đa có thể được nội tuyến. Tôi sẽ chỉ ra rằng đây là lý do tại sao Java thích có nhiều phương thức nhỏ - bởi vì chúng có thể dễ dàng được nội tuyến hóa. Những phương thức nhỏ đó trở nên nhanh hơn khi chúng được gọi nhiều hơn vì chúng có thể được nội tuyến. Và trong khi người ta có thể chơi với con số đó và làm cho nó lớn hơn, nó có thể khiến các tối ưu hóa khác kém hiệu quả hơn. (câu hỏi SO liên quan: Chiến lược nội tuyến JIT của HotSpot , trong đó chỉ ra một số tùy chọn khác để xem qua nội bộ của nội tuyến mà HotSpot đang thực hiện).
Vì vậy, không - mã không được nội tuyến tại thời gian biên dịch. Và, vâng - mã rất có thể được nội tuyến trong thời gian chạy nếu tối ưu hóa hiệu suất bảo đảm nó.
Và tất cả những gì tôi đã viết về HotSpot inlining chỉ áp dụng cho HotSpot JVM do Oracle phân phối. Nếu bạn xem danh sách các máy ảo Java của wikipedia, có nhiều thứ khác ngoài HotSpot và cách các JVM xử lý nội tuyến có thể hoàn toàn khác với những gì tôi đã mô tả ở trên. Apache Harmony, Dalvik, ART - mọi thứ có thể hoạt động khác nhau ở đó.