2013-08-14 72 views
13

Trong một trong những ứng dụng của tôi, tôi đang sử dụng như sau:Class.forName() bộ nhớ đệm

public void calculate (String className) 
{ 
    ... 
    Class clazz = Class.forName(className); 
    ... 
} 

Chức năng này được gọi là nhiều lần/giây. Có khoảng 10 tên lớp có thể. Và trong khi tôi nhận ra có một số bộ nhớ đệm nội bộ bên trong chức năng này, Tôi nghĩ rằng bộ nhớ đệm này chỉ có sẵn ở cấp bản địa.

Vì lý do này, tôi bắt đầu tự hỏi liệu tôi có nên thêm bộ nhớ đệm của riêng mình hay không.

private static Map<String,Class> classMap; 

public void calculate (String className) 
{ 
    ... 
    Class clazz = classMap.get(className); 
    if (clazz == null) 
    { 
     clazz = Class.forName(className); 
     if (classMap == null) classMap = new HashMap<String, Class>(40); 
     classMap.put(className, clazz); 
    } 
    ... 
} 

Đây có phải là lợi ích hiệu suất hay không thực sự tạo nên sự khác biệt?

Cảm ơn bạn trước

+1

Tôi có thể nghĩ ra một cách để tìm ra ... – joev

+1

Bạn có hồ sơ ứng dụng của mình không? Nó có chứng minh là một nút chai? Nếu không, tôi sẽ không triển khai bộ nhớ cache cho đến khi cần. – Thomas

+0

Bạn có thấy các chuỗi đã bị chặn trong một kết xuất chuỗi không? Tôi thấy chúng trong khi lược tả ứng dụng của tôi, vì vậy tôi nghĩ Class.forName() đáng để lưu vào bộ nhớ đệm. – mparaz

Trả lời

9

Tôi đã viết một kịch bản nhỏ để tính toán thời gian thực hiện của cả hai chức năng.

Đây là lớp Chính mà tôi đã sử dụng.

public class Benchmark 
{ 
    public static void main(String... pArgs) 
    { 
    // prepare all data as much as possible. 
    // we don't want to do this while the clock is running. 
    Class[] classes = {Object.class, Integer.class, String.class, Short.class, Long.class, Double.class, 
         Float.class, Boolean.class, Character.class, Byte.class}; 
    int cycles = 1000000; 
    String[] classNames = new String[cycles]; 
    for (int i = 0; i < cycles; i++) 
    { 
     classNames[i] = classes[i % classes.length].getName(); 
    } 

    // THERE ARE 2 IMPLEMENTATIONS - CLASSIC vs CACHING 
    Implementation impl = new Caching(); // or Classic(); 

    // Start the clocks ! 
    long startTime = System.currentTimeMillis(); 
    for (int i = 0; i < cycles; i++) 
    { 
     impl.doStuff(classNames[i]); 
    } 
    long endTime = System.currentTimeMillis(); 

    // calculate and display result 
    long totalTime = endTime - startTime; 
    System.out.println(totalTime); 
    } 
} 

Đây là việc thực hiện cổ điển sử dụng Class.forName

private interface Implementation 
    { 
    Class doStuff(String clzName); 
    } 

    private static class Classic implements Implementation 
    { 
    @Override 
    public Class doStuff(String clzName) 
    { 
     try 
     { 
     return Class.forName(clzName); 
     } 
     catch (Exception e) 
     { 
     return null; 
     } 
    } 
    } 

Đây là việc thực hiện thứ hai sử dụng một HashMap để cache Class đối tượng.

private static class Caching implements Implementation 
    { 
    private Map<String, Class> cache = new HashMap<String, Class>(); 

    @Override 
    public Class doStuff(String clzName) 
    { 
     Class clz = cache.get(clzName); 
     if (clz != null) return clz; 
     try 
     { 
     clz = Class.forName(clzName); 
     cache.put(clzName, clz); 
     } 
     catch (Exception e) 
     { 
     } 
     return clz; 
    } 
    } 

Kết quả:

  • 1100 ms mà không cần bộ nhớ đệm.
  • chỉ 15 mili giây với bộ nhớ đệm.

Kết luận:

  • Có một sự khác biệt đáng kể -> có!
  • Ứng dụng của tôi có quan trọng không - không hề.
+2

Lưu ý rằng bạn không nên sử dụng 'currentTimeMillis' để đo điểm chuẩn, và bạn nên bao gồm một thời gian khởi động để biên dịch JIT vv Nó có thể sẽ không thay đổi kết quả * rất nhiều * nhưng đáng để biết. Tôi sẽ không bắt được ngoại lệ ở đây, hoặc là ... –

+1

Tôi vừa mới tự mình chạy các tiêu chí chuẩn và thấy rằng nếu các lớp tôi đang tìm là những lớp được định nghĩa bởi trình nạp lớp hiện tại (chứ không phải một số lớp "cốt lõi") thì nhanh hơn khoảng 10% trong trường hợp chậm ... có lẽ vì nó không được ủy quyền. Vẫn chậm hơn phiên bản bộ nhớ đệm, nhưng không phải là xấu. –

+0

Chỉ cần tự hỏi ... tôi nên sử dụng những gì thay vì currentTimeMillis? Tôi không bao giờ sử dụng nó trước khi phải trung thực, nhưng đã nhận nó từ một số chủ đề stackoverflow khác. Vào thời kỳ khởi động, bạn có nghĩa là một Thread.sleep đơn giản (10000), phải không? - Cảm ơn các ý kiến ​​ – bvdb

8

Đây sẽ là một hiệu suất đạt được hay nó thực sự làm cho có sự khác biệt?

Tôi sẽ ngạc nhiên nếu nó tạo ra sự khác biệt đáng kể - và nếu bạn chỉ gọi nó là "vài lần mỗi giây" (thay vì, một triệu) thì thực sự không đáng để tối ưu hóa.

Bạn nên ít nhất thử cách ly này trong tiêu chuẩn trước khi cam kết thiết kế phức tạp hơn này. Tôi thực sự mong đợi Class.forName sẽ được lưu vào bộ nhớ cache này và việc thêm độ phức tạp hơn vào ứng dụng của bạn sẽ không tốt.

+0

+1 Tôi nghi ngờ một điểm chuẩn vi mô sẽ cho thấy một sự khác biệt nhưng trong một ứng dụng thực sự, tôi sẽ rất ngạc nhiên nếu nó đã làm. –

+0

Cảm ơn câu trả lời của bạn Jon. Tôi đã lấy lời khuyên của bạn và đã làm một số điểm chuẩn, và thêm các kết quả ở đây là tốt. Cảm ơn. – bvdb

0

Không, bạn không nên. Class.forName sẽ không tải cùng một lớp hai lần nhưng sẽ cố gắng tìm lớp trong số các lớp được nạp. Nó được thực hiện ở cấp độ bản địa và được cho là rất hiệu quả.

2

Class.forName() thực hiện hai điều:

  1. rồi nó lấy một lớp được nạp từ classloader
  2. nếu không có lớp học như vậy được tìm thấy, nó sẽ cố gắng để tải nó.

Phần # 1 khá nhanh. # 2 là nơi mà công việc thực sự bắt đầu (nơi JVM có thể nhấn vào đĩa cứng hoặc thậm chí mạng, tùy thuộc vào trình nạp lớp). Và nếu bạn chuyển các tham số tương tự vào, thì tất cả, nhưng các lời gọi đầu tiên sẽ không bao giờ đến bướC# 2.

Vì vậy, không: đó là có thể là không đáng tối ưu hóa.

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