2009-12-01 35 views
9

Tôi hiểu rằng kích thước perm được sử dụng để lưu trữ dữ liệu meta, bao gồm mã byte, nội dung tĩnh, v.v.Phản ánh ảnh hưởng đến kích thước Perm như thế nào?

Câu hỏi của tôi về cách sử dụng phản ánh ảnh hưởng đến kích thước uốn, nếu có. Ý tôi là nếu Program-A sử dụng cách thức thông thường của các đối tượng đang chạy và Program-B sử dụng phản ánh trên, cách thức kích thước perm của hai chương trình so sánh?

Trả lời

7

Khoảng trống sẽ phát triển khi bạn thực thi mã sẽ tải các lớp mới hoặc nội bộ hóa chuỗi. Các lớp phản chiếu phải được tải đó là chắc chắn. Tôi không chắc liệu API phản chiếu có sử dụng các chuỗi được mã hóa nội bộ hay không nhưng không khó để tìm ra.

Ví dụ cho phương thức getDeclaredMethod(String name, Class<?>... parameterTypes) thông số tên sẽ được nội bộ hóa.

đoạn này sẽ độc đáo đầy lên không gian perm:

Random random = new Random(); 
while(true){ 
    try { 
     X.class.getDeclaredMethod(toBinaryString(random.nextLong())); 
    } catch (Exception e) { 
    } 
} 

alt text http://img4.imageshack.us/img4/9725/permgen.jpg

Trong một ứng dụng thực tế nó sẽ làm cho không có sự khác biệt.

+0

Điều tôi tin rằng bạn đang thấy ở đó đang thực hiện tìm kiếm tên để '==' có thể được sử dụng trên các tên phương thức đã được tập trung. (Không thuyết phục đó là một ý tưởng tuyệt vời.) Trong khuôn mặt, sự phản chiếu có thể tạo ra các lớp (đôi khi). –

+0

(Những chuỗi nội bộ có thể được GCable với chu trình thu gom rác đầy đủ, giả sử chất lượng thực hiện tốt.) –

+0

@Tom, bạn có thể cung cấp một ví dụ duy nhất trong đó lớp được tạo do phản ánh không? Tôi không cố gắng là một nỗi đau, tôi thực sự muốn biết về những trường hợp này như tôi chưa từng thấy. – PSpeed

3

Tôi hoàn toàn không chắc mình hiểu câu hỏi nhưng các lớp Java đã bao gồm mọi thứ cần phải thực hiện sự phản chiếu trên chúng. Tệp .class không thay đổi dựa trên cách nó được sử dụng.

Chỉnh sửa 1: Cung cấp một số ví dụ cụ thể hơn để phân biệt tốt hơn loại "phản chiếu" mà chúng ta đang nói đến.

Ví dụ:

class Foo { 
    public void bar() { 
     System.out.println("Hello, world."); 
    } 
} 

// Conventional calling 
Foo f = new Foo(); 
foo.bar(); 

// Reflection based callling 
Class fooClass = Class.forName("Foo"); 
Method m = fooClass.getMethod("bar", null); 
Object f = fooClass.newInstance(); 
m.invoke(m, null);  

Trong trường hợp gọi điện thoại thông thường, Foo.class và bất kỳ phụ thuộc lớp trực tiếp của nó sẽ được nạp. thanh sẽ được thực hiện. Các chuỗi "Foo" và "bar" sẽ đã được thực thi như một phần của lớp gọi bởi vì mã byte sử dụng các chuỗi để giải quyết các lớp và các phương thức trong thời gian chạy. (thực sự "bar" sẽ là chữ ký đầy đủ của phương thức, vì vậy thực sự dài hơn "thanh")

Trong trường hợp phản chiếu, chính xác điều tương tự cũng xảy ra. Lớp bổ sung duy nhất được nạp là Method.class. Đó sẽ là ảnh hưởng duy nhất đến kích thước perm.

Trường hợp thứ hai có ý nghĩa về hiệu suất. Phương pháp tra cứu tương đối đắt tiền nên bạn nên sử dụng các đối tượng Phương thức cache khi có thể. Cuộc gọi phương thức bổ sung để gọi có một hàm ý hiệu suất nhỏ vì đó là một cuộc gọi phương thức bổ sung. Điểm phát sóng sẽ gặp sự cố khi tối ưu hóa thông qua cuộc gọi này ... ít nhất là hơn bình thường. JITing xảy ra chính xác như nhau.

Chỉnh sửa 2: Ghi nhận một số đối tượng bổ sung được nạp trong quá trình phản ánh ...

java.lang.Class sẽ tạo ra và đối tượng Method cache (hoặc đối tượng Field, vv) khi truy cập. Đây là những bộ nhớ đệm trong một SoftReference và do đó sẽ được khai hoang nếu sử dụng bộ nhớ đòi hỏi nó.

Tuy nhiên, việc khởi tạo các đối tượng này có nghĩa là có thể có thêm chuỗi được quốc tế tải bởi máy ảo để hỗ trợ tạo các đối tượng Phương thức này. Tôi đoán là những sợi dây này có thể đã là một phần của hồ bơi không đổi của lớp phản xạ nhưng có thể chúng không phải vậy. Dù bằng cách nào, đó là một lần nhấn duy nhất cho mỗi phương thức mỗi lớp, mỗi trường cho mỗi lớp, vv .. Truy cập các phương thức, bạn sẽ nhận được ít nhất tất cả các tên cho các phương thức đó được thực hiện. Truy cập vào các lĩnh vực, bạn sẽ nhận được tên của những lĩnh vực intern'ed.

+0

Các lớp học có thể được tạo động bởi việc triển khai phản chiếu để truy cập nhanh. –

+0

Tham khảo xin vui lòng? Tôi sử dụng sự phản ánh tất cả thời gian (thực sự ... tất cả thời gian) và chưa bao giờ thấy một lớp duy nhất được tạo ra như là một kết quả. Thư viện của bên thứ ba có thể thực hiện một số nội dung và công cụ proxy động chắc chắn sẽ tạo ra một lớp thời gian chạy. Nhưng chỉ đơn giản là gọi phương thức trên một Class/Object không nên. – PSpeed

+0

@ PSpeed..pls kiểm tra liên kết sau https://jaxb.dev.java.net/issues/show_bug.cgi?id=581 –

2

Câu trả lời đầu tiên về cơ bản là đúng - cho dù một lớp được tải 'bình thường' hay thông qua phản ánh sẽ không có sự khác biệt. Dù bằng cách nào nó được nạp. Tôi không thể nghĩ ra bất kỳ sự khác biệt nào trong các chuỗi nội bộ mà tôi biết.

Tôi cho rằng có hiệu ứng gián tiếp hơn: sử dụng phản chiếu, bạn có thể khiến ít lớp hơn được tải. Giả sử bạn có một số mã tải một trong 100 lớp dựa trên một số tham số. Viết nó mà không có sự phản ánh, bạn sẽ nhập tất cả 100 lớp và khởi tạo một trong số chúng.

Với sự phản ánh, chỉ có 1 lớp sẽ được tải. Nếu không có, tại thời điểm lớp với mã này tải, tất cả 100 lớp được nhập sẽ tải vì tất cả đều được tham chiếu. Vì vậy, nhiều lớp học được tải vào không gian perm.

Nhưng đây là một ví dụ hơi giả vờ. Trừ khi nó thực sự mô tả tình hình của bạn, tôi tưởng tượng bạn sẽ nhận thấy hầu như không có sự khác biệt. Đó là, cho đến khi bạn chắc chắn nó không tầm thường, đừng để điều này ảnh hưởng đến quyết định thiết kế của bạn.

3

Phản ánh có thể tạo các lớp, tùy thuộc vào việc triển khai. Bạn có thể, ví dụ, cần phải sử dụng một tạo tác phản chiếu hàng ngàn lần trước khi nó được biên dịch sang bytecode.

Như mọi khi, lời khuyên là: Lập hồ sơ cho mã của bạn trong các tình huống thực tế.

+0

Bạn có nghĩa là "mã gốc" ở trên không? Các lớp đã là mã byte. Hotspot sẽ gặp sự cố khi tối ưu hóa thông qua các cuộc gọi phản chiếu nhưng JIT hoạt động giống nhau. – PSpeed

+0

Không, ý tôi là bytecode. Rõ ràng sau khi sử dụng HotSpot sẽ biên dịch bytecode thành native. Các trường có thể được đọc bằng cách tìm bù đắp từ đầu của đối tượng và thêm. Chúng cũng có thể được đọc bằng cách viết mã byte đọc các trường đó (bỏ qua các đặc quyền truy cập). Một cái gì đó như thế cứ tiếp diễn (trong Sun JRE), nhưng tôi quên đi các chi tiết. –

+0

+1: Chỉ cần kích hoạt nhật ký GC và kiểm tra System.out - [Unloading class sun.reflect.GeneratedMethodAccessor1403] [Unloading class sun.reflect.GeneratedConstructorAccessor446] [Unloading class sun.reflect.GeneratedMethodAccessor562] - Chúng được tạo ra bởi sự phản chiếu thực hiện. – fglez

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