2009-04-16 20 views
16

Tôi cần lập trình tìm ra chính xác bộ nhớ mà một đối tượng Java đã cho chiếm bao gồm bộ nhớ bị chiếm bởi các đối tượng mà nó đang tham chiếu.Lập trình tính toán bộ nhớ bị chiếm bởi một đối tượng Java bao gồm các đối tượng mà nó tham chiếu

Tôi có thể tạo vùng lưu trữ bộ nhớ và phân tích kết quả bằng công cụ. Tuy nhiên phải mất một số lượng đáng kể thời gian để tạo ra một bãi chứa đống và cho một công cụ như vậy để đọc kết xuất để tạo báo cáo. Với thực tế rằng tôi có thể sẽ cần phải làm điều này nhiều lần, công việc của tôi sẽ nhanh hơn rất nhiều nếu tôi có thể ném vào một số mã trong dự án của tôi mà sẽ cho tôi giá trị "thời gian chạy" này.

Làm cách nào để đạt được điều này tốt nhất?

ps: Cụ thể là tôi có một bộ sưu tập của các đối tượng của loại javax.xml.transform.Templates

+0

Tôi sẽ không gọi đó là bản dupe chính xác. – Inisheer

+0

Vâng, không chính xác là một bản dupe, vì câu hỏi ban đầu không cung cấp câu trả lời có thể sử dụng được trong ngữ cảnh này ... – Varkhan

+0

Nó cung cấp một số. – erickson

Trả lời

11

Bạn sẽ cần phải sử dụng phản ánh cho điều đó. Đoạn mã kết quả quá phức tạp để tôi đăng ở đây (mặc dù nó sẽ sớm có sẵn như một phần của bộ công cụ GPL mà tôi đang xây dựng), nhưng ý tưởng chính là:

  • Tiêu đề đối tượng sử dụng 8 byte (đối với con trỏ lớp và số tham chiếu)
  • Mỗi trường nguyên thủy sử dụng 1, 2, 4 hoặc 8 byte tùy thuộc vào loại thực tế
  • Mỗi trường tham chiếu (tức là không nguyên thủy) sử dụng 4 byte (tham chiếu, cộng với mọi thứ sử dụng đối tượng được tham chiếu)

Bạn cần xử lý riêng các mảng (8 tes của tiêu đề, 4 byte của trường độ dài, 4 * byte độ dài của bảng, cộng với bất kỳ đối tượng bên trong đang sử dụng). Bạn cần xử lý các kiểu đối tượng khác lặp qua các trường (và các trường cha mẹ của nó) bằng cách sử dụng sự phản chiếu.

Bạn cũng cần giữ một tập hợp các đối tượng "nhìn thấy" trong quá trình đệ quy, để không tính nhiều lần đối tượng được tham chiếu ở một vài nơi.

+2

Đối với những gì nó có giá trị, tiêu đề đối tượng sử dụng ít nhất là nhiều, nhưng tôi nghĩ rằng JVM cũ hơn sử dụng nhiều hơn nữa. Đó là dễ dàng (đủ) để xác minh, quá, bằng mã kiểm tra đơn giản (tạo ra một triệu đối tượng, gc, ngủ một chút, kiểm tra bộ nhớ miễn phí - không đảm bảo về mặt lý thuyết để làm việc, trong thực tế). – StaxMan

+2

Ngoài ra: 4 byte dành cho tài liệu tham khảo 32 bit. Đối với hệ thống 64-bit 8 byte tôi nghĩ. – StaxMan

+0

Tôi cũng đã nghĩ như vậy trong một thời gian, nhưng không thể tìm thấy bất kỳ tham chiếu nào về điều đó ... vì vậy tôi đã thử một số điểm chuẩn như StaxMan mô tả, và có vẻ như nó vẫn còn 4 ... đi con số. – Varkhan

3

Một giải pháp chung tốt là sử dụng đồng bằng kích thước heap. Điều này liên quan đến nỗ lực tối thiểu và có thể sử dụng lại được giữa bất kỳ loại đồ thị đối tượng/đối tượng nào. Bởi instantiating và phá hủy các đối tượng của bạn nhiều lần và thu gom rác thải ở giữa, và sau đó lấy trung bình, bạn tránh trình biên dịch và tối ưu hóa JVM thay đổi kết quả và nhận được một kết quả khá chính xác. Nếu bạn cần một câu trả lời CHÍNH XÁC xuống byte thì đây có thể không phải là giải pháp cho bạn, nhưng đối với tất cả các ứng dụng thực tế mà tôi biết (profiling, calcualtions yêu cầu bộ nhớ) nó hoạt động rất tốt. Mã dưới đây sẽ làm điều đó.

public class Sizeof { 
     public static void main(String[] args) 
      throws Exception { 
     // "warm up" all classes/methods that we are going to use: 
     runGC(); 
     usedMemory(); 

     // array to keep strong references to allocated objects: 
     final int count = 10000; // 10000 or so is enough for small ojects 
     Object[] objects = new Object[count]; 

     long heap1 = 0; 

     // allocate count+1 objects, discard the first one: 
     for (int i = -1; i < count; ++i) { 
      Object object; 

    //// INSTANTIATE YOUR DATA HERE AND ASSIGN IT TO 'object': 


      object=YOUR OBJECT; 
    ////end your code here 
      if (i >= 0) { 
      objects[i] = object; 
      } 
      else { 
      object = null; // discard the "warmup" object 
      runGC(); 
      heap1 = usedMemory(); // take a "before" heap snapshot 
      } 
     } 

     runGC(); 
     long heap2 = usedMemory(); // take an "after" heap snapshot: 

     final int size = Math.round(((float)(heap2 - heap1))/count); 
     System.out.println("'before' heap: " + heap1 + 
          ", 'after' heap: " + heap2); 
     System.out.println("heap delta: " + (heap2 - heap1) + 
          ", {" + objects[0].getClass() + "} size = " + size + " bytes"); 
     } 

     // a helper method for creating Strings of desired length 
     // and avoiding getting tricked by String interning: 
     public static String createString(final int length) { 
     final char[] result = new char[length]; 
     for (int i = 0; i < length; ++i) { 
      result[i] = (char)i; 
     } 

     return new String(result); 
     } 

     // this is our way of requesting garbage collection to be run: 
     // [how aggressive it is depends on the JVM to a large degree, but 
     // it is almost always better than a single Runtime.gc() call] 
     private static void runGC() 
      throws Exception { 
     // for whatever reason it helps to call Runtime.gc() 
     // using several method calls: 
     for (int r = 0; r < 4; ++r) { 
      _runGC(); 
     } 
     } 

     private static void _runGC() 
      throws Exception { 
     long usedMem1 = usedMemory(), usedMem2 = Long.MAX_VALUE; 

     for (int i = 0; (usedMem1 < usedMem2) && (i < 1000); ++i) { 
      s_runtime.runFinalization(); 
      s_runtime.gc(); 
      Thread.currentThread().yield(); 

      usedMem2 = usedMem1; 
      usedMem1 = usedMemory(); 
     } 
     } 

     private static long usedMemory() { 
     return s_runtime.totalMemory() - s_runtime.freeMemory(); 
     } 

     private static final Runtime s_runtime = Runtime.getRuntime(); 

    } // end of class 
+1

Điều này là thiếu sót do thực tế rằng các phương thức gc() và runFinalization() không xác định. Tức là: chúng chỉ là gợi ý cho thời gian chạy để thực hiện những hành động đó. Nó hoàn toàn hợp pháp cho một thời gian chạy để bỏ qua chúng. –

+1

nó không phải là thiếu sót - họ sẽ bị bỏ qua đôi khi bạn nói đó là lý do tại sao số lượng lớn các lần lặp lại và tính trung bình của kết quả. – Peter

+1

Bạn có thể có ít nhất ghi nhận nguồn của mã này, ngay cả khi bạn đã tinh chỉnh một chút: http://www.javaworld.com/javaworld/javatips/jw-javatip130.html – wolfcastle

6

Có vẻ như đã có tiện ích để thực hiện việc này được gọi là Classmexer.

Tôi chưa thử bản thân mình, nhưng tôi sẽ đi tuyến đường đó trước khi tôi tự mình cuộn.

+1

+1 cho liên kết. –

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