2016-11-08 22 views
7

nói rằng tôi có 2 trường hợp cùng một lớp, nhưng chúng hoạt động khác nhau (theo các đường dẫn mã khác nhau) dựa trên trường boolean cuối cùng được đặt tại thời gian xây dựng. nên cái gì như:các JVM có thể điều chỉnh tối ưu các phiên bản khác nhau của cùng một lớp khác nhau?

public class Foo { 
    private final boolean flag; 

    public Foo(boolean flagValue) { 
     this.flag = flagValue; 
    } 

    public void f() { 
     if (flag) { 
     doSomething(); 
     } else { 
     doSomethingElse(); 
     } 
    } 
} 

2 trường hợp của Foo với các giá trị khác nhau cho flag thể về mặt lý thuyết được hỗ trợ bởi 2 hội khác nhau, do đó loại bỏ các chi phí của if (xin lỗi vì sự dụ giả tạo, nó đơn giản nhất tôi có thể đi lên với).

vì vậy câu hỏi của tôi là - làm bất kỳ JVM nào thực sự thực hiện việc này? hoặc là một lớp duy nhất luôn luôn được hỗ trợ bởi một hội đồng duy nhất?

+1

Những gì bạn mô tả âm thanh rất khủng khiếp như phân lớp, bạn có thể làm rõ lý do tại sao điều đó sẽ không hoạt động? Tôi không thách thức chỉ là cố gắng hiểu ranh giới của vấn đề. – Taylor

+0

Tất cả các trường hợp đều có cùng mã. Câu hỏi của bạn không có ý nghĩa. – EJP

+2

@Taylor Xin lỗi nếu tôi dám giải thích OP, nhưng tôi đoán rằng những gì anh ta/cô ấy hỏi không phải là một mẫu phù hợp (mà rõ ràng là thừa kế), nhưng nếu JVM đủ "thông minh" để loại bỏ việc sử dụng 'flag' biến và tạo ra hai bytecode khác nhau cho toàn bộ lớp (một tương ứng với các nhánh' true', và cái kia cho các 'false'). –

Trả lời

7

Có, JVM thực hiện hình thức tối ưu hóa này. Trong trường hợp của bạn, điều này sẽ là kết quả của inlining and adaptive optimization vì là một giá trị luôn đúng. Xét đoạn mã sau:

Foo foo = new Foo(true); 
foo.f(); 

Đó là tầm thường để chứng minh cho HotSpot rằng Foo luôn là một ví dụ thực tế của Foo tại trang web tiếng gọi của f những gì cho phép máy ảo để chỉ cần sao chép-dán mã của phương pháp này, do đó loại bỏ công văn ảo. Sau khi nội tuyến, ví dụ được giảm xuống:

Foo foo = new Foo(true); 
if (foo.flag) { 
    doSomething(); 
} else { 
    doSomethingElse(); 
} 

này một lần nữa, cho phép để giảm mã để:

Foo foo = new Foo(true); 
foo.doSomething(); 

Nếu việc tối ưu hóa có thể được áp dụng do đó không phụ thuộc vào monomorphism của các trang web tiếng gọi của foo và sự ổn định của flag tại trang web cuộc gọi này. (VM cấu hình các phương thức của bạn cho các mẫu như vậy.) VM ít có khả năng dự đoán kết quả của chương trình của bạn, tối ưu hóa ít hơn được áp dụng.

Nếu ví dụ quá tầm thường như mã trên, thì JIT có lẽ cũng sẽ xóa phân bổ đối tượng và chỉ cần gọi doSomething. Ngoài ra, đối với trường hợp ví dụ nhỏ, trong đó giá trị của trường có thể được chứng minh là true không đáng kể, VM thậm chí không cần tối ưu hóa thích nghi mà chỉ áp dụng tối ưu hóa ở trên. Có một công cụ tuyệt vời có tên là JITWatch cho phép bạn xem xét cách mã của bạn được tối ưu hóa.

+2

Trong trường hợp cụ thể này, JVM thậm chí có thể tạo ra kết quả bằng phân tích tĩnh mà không có dữ liệu lược tả. Nó sẽ trở nên trực quan hơn bằng cách hiển thị kết quả của việc nội tuyến mã của nhà xây dựng, vì kết quả về cơ bản là 'foo.flag = true; if (foo.flag)… 'thậm chí không yêu cầu' flag' là 'final' để được tối ưu hóa. Tùy thuộc vào mã theo dõi, nó thậm chí sẽ không tạo ra cá thể 'Foo' cho đường dẫn mã cụ thể này. – Holger

5

Các điều sau đây áp dụng cho điểm phát sóng, các JVM khác có thể áp dụng các tối ưu hóa khác nhau.

Nếu những trường hợp đang quay gán cho static final lĩnh vực và sau đó được gọi bằng mã khác và VM được bắt đầu với -XX:+TrustFinalNonStaticFields sau đó những trường hợp có thể tham gia vào gấp liên tục và nội tuyến CONSTANT.f() có thể dẫn đến các chi nhánh khác nhau được loại bỏ.

Một cách tiếp cận khác có sẵn cho mã đặc quyền là tạo lớp ẩn danh thay vì trường hợp qua sun.misc.Unsafe.defineAnonymousClass(Class<?>, byte[], Object[]) và vá một hằng số lớp cho mỗi lớp, nhưng cuối cùng cũng phải được tham chiếu thông qua một hằng số lớp để có bất kỳ ảnh hưởng nào đến tối ưu hóa.

Các vấn đề liên quan