Trong Java, cách tốt nhất để xác định kích thước của một đối tượng là gì?


617

Tôi có một ứng dụng đọc tệp CSV với hàng đống dữ liệu. Tôi cung cấp cho người dùng một bản tóm tắt về số lượng hàng dựa trên các loại dữ liệu, nhưng tôi muốn đảm bảo rằng tôi không đọc quá nhiều hàng dữ liệu và gây ra OutOfMemoryErrors. Mỗi hàng dịch thành một đối tượng. Có một cách dễ dàng để tìm ra kích thước của đối tượng đó theo chương trình? Có một tài liệu tham khảo xác định mức độ lớn của các kiểu nguyên thủy và tham chiếu đối tượng cho a VMkhông?

Ngay bây giờ, tôi có mã cho biết đọc tới 32.000 hàng , nhưng tôi cũng muốn có mã cho biết đọc càng nhiều hàng càng tốt cho đến khi tôi sử dụng 32 MB bộ nhớ. Có lẽ đó là một câu hỏi khác, nhưng tôi vẫn muốn biết.


Tôi đã thêm Đại lý của mình với cấu hình mvn và giải thích cách thức ở đây: stackoverflow.com/a/36102269/711855
juanmf

Câu trả lời:


461

Bạn có thể sử dụng gói java.lang.instrument

Biên dịch và đặt lớp này trong JAR:

import java.lang.instrument.Instrumentation;

public class ObjectSizeFetcher {
    private static Instrumentation instrumentation;

    public static void premain(String args, Instrumentation inst) {
        instrumentation = inst;
    }

    public static long getObjectSize(Object o) {
        return instrumentation.getObjectSize(o);
    }
}

Thêm phần sau vào MANIFEST.MF:

Premain-Class: ObjectSizeFetcher

Sử dụng getObjectSize:

public class C {
    private int x;
    private int y;

    public static void main(String [] args) {
        System.out.println(ObjectSizeFetcher.getObjectSize(new C()));
    }
}

Gọi với:

java -javaagent:ObjectSizeFetcherAgent.jar C

2
@Stefan Gợi ý hay! Bạn có thể xin vui lòng cho, những gì sẽ là kích thước của byte[0], byte[1], byte[5], int[0], int[1], int[2]sử dụng phương pháp này bạn mô tả? Sẽ thật tuyệt, nếu kết quả bao gồm cả chi phí cho độ dài của việc sắp xếp mảng và bộ nhớ.
dma_k

8
Tôi đã thử điều này và nhận được kết quả kỳ lạ và không có ích. Chuỗi luôn luôn là 32, bất kể kích thước. Tôi nghĩ rằng đây có thể là kích thước con trỏ nhưng đối với một lớp bất biến khác mà tôi đã tạo, tôi có 24. Nó hoạt động tốt cho người nguyên thủy nhưng sau đó bạn không thực sự cần một chương trình để cho bạn biết một char lớn như thế nào.
Brel

6
@Brel giải pháp này chỉ là "xấp xỉ lượng lưu trữ được tiêu thụ bởi đối tượng đã chỉ định", như được chỉ định trong tài liệu. Ngoài ra, tôi cho rằng các tác giả đã quyết định đặt kích thước của Chuỗi là 32 byte (chỉ con trỏ?) Vì nhóm Chuỗi của Java, điều này gây khó khăn khi nói, liệu một cá thể Chuỗi được chia sẻ (được lưu trữ trong nhóm) hay địa phương & duy nhất cho một lớp học.
Andrei I

11
Làm cách nào tôi có thể sử dụng ObjectSizeFetcher, nếu không xuất jar? Tôi đã thử nghiệm dự án java trong nhật thực.
Yura Shinkarev

3
@brel Lý do Chuỗi chỉ có 32 byte bất kể độ dài thực tế là do phần có độ dài thay đổi của chuỗi được lưu trữ trong char [], đây là đối tượng của chính nó. Để có được kích thước thật của một đối tượng, bạn cần thêm kích thước của chính nó và kích thước của từng đối tượng mà nó tham chiếu.
tombrown52

117

Bạn nên sử dụng jol , một công cụ được phát triển như một phần của dự án OpenJDK.

JOL (Bố cục đối tượng Java) là hộp công cụ nhỏ để phân tích các sơ đồ bố trí đối tượng trong các JVM. Các công cụ này đang sử dụng rất nhiều Unsafe, JVMTI và Servicilityility Agent (SA) để giải mã bố cục, dấu chân và tham chiếu đối tượng thực tế. Điều này làm cho JOL chính xác hơn nhiều so với các công cụ khác dựa trên các đống, các giả định đặc tả, v.v.

Để có được kích thước của các nguyên hàm, tham chiếu và các phần tử mảng, sử dụng VMSupport.vmDetails(). Trên Oracle JDK 1.8.0_40 chạy trên Windows 64 bit (được sử dụng cho tất cả các ví dụ sau), phương thức này trả về

Running 64-bit HotSpot VM.
Using compressed oop with 0-bit shift.
Using compressed klass with 3-bit shift.
Objects are 8 bytes aligned.
Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

Bạn có thể lấy kích thước nông của một đối tượng bằng cách sử dụng ClassLayout.parseClass(Foo.class).toPrintable()(tùy ý chuyển một thể hiện đến toPrintable). Đây chỉ là không gian được sử dụng bởi một thể hiện duy nhất của lớp đó; nó không bao gồm bất kỳ đối tượng nào khác được tham chiếu bởi lớp đó. Nó không bao gồm VM overhead cho tiêu đề đối tượng, liên kết trường và padding. Dành cho java.util.regex.Pattern:

java.util.regex.Pattern object internals:
 OFFSET  SIZE        TYPE DESCRIPTION                    VALUE
      0     4             (object header)                01 00 00 00 (0000 0001 0000 0000 0000 0000 0000 0000)
      4     4             (object header)                00 00 00 00 (0000 0000 0000 0000 0000 0000 0000 0000)
      8     4             (object header)                cb cf 00 20 (1100 1011 1100 1111 0000 0000 0010 0000)
     12     4         int Pattern.flags                  0
     16     4         int Pattern.capturingGroupCount    1
     20     4         int Pattern.localCount             0
     24     4         int Pattern.cursor                 48
     28     4         int Pattern.patternLength          0
     32     1     boolean Pattern.compiled               true
     33     1     boolean Pattern.hasSupplementary       false
     34     2             (alignment/padding gap)        N/A
     36     4      String Pattern.pattern                (object)
     40     4      String Pattern.normalizedPattern      (object)
     44     4        Node Pattern.root                   (object)
     48     4        Node Pattern.matchRoot              (object)
     52     4       int[] Pattern.buffer                 null
     56     4         Map Pattern.namedGroups            null
     60     4 GroupHead[] Pattern.groupNodes             null
     64     4       int[] Pattern.temp                   null
     68     4             (loss due to the next object alignment)
Instance size: 72 bytes (reported by Instrumentation API)
Space losses: 2 bytes internal + 4 bytes external = 6 bytes total

Bạn có thể có được một cái nhìn tóm tắt về kích thước sâu của một đối tượng sử dụng GraphLayout.parseInstance(obj).toFootprint(). Tất nhiên, một số đối tượng trong dấu chân có thể được chia sẻ (cũng được tham chiếu từ các đối tượng khác), do đó, đó là sự ước tính quá mức của không gian có thể được thu hồi khi đối tượng đó được thu gom rác. Đối với kết quả của Pattern.compile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$")(lấy từ câu trả lời này ), jol báo cáo tổng số dấu chân là 1840 byte, trong đó chỉ có 72 là bản thể Mẫu.

java.util.regex.Pattern instance footprint:
     COUNT       AVG       SUM   DESCRIPTION
         1       112       112   [C
         3       272       816   [Z
         1        24        24   java.lang.String
         1        72        72   java.util.regex.Pattern
         9        24       216   java.util.regex.Pattern$1
        13        24       312   java.util.regex.Pattern$5
         1        16        16   java.util.regex.Pattern$Begin
         3        24        72   java.util.regex.Pattern$BitClass
         3        32        96   java.util.regex.Pattern$Curly
         1        24        24   java.util.regex.Pattern$Dollar
         1        16        16   java.util.regex.Pattern$LastNode
         1        16        16   java.util.regex.Pattern$Node
         2        24        48   java.util.regex.Pattern$Single
        40                1840   (total)

Thay vào đó GraphLayout.parseInstance(obj).toPrintable(), nếu bạn sử dụng , jol sẽ cho bạn biết địa chỉ, kích thước, loại, giá trị và đường dẫn của các cuộc hội thảo trường đến từng đối tượng được tham chiếu, mặc dù điều đó thường quá nhiều chi tiết không hữu ích. Đối với ví dụ mẫu đang diễn ra, bạn có thể nhận được những điều sau đây. (Địa chỉ có thể sẽ thay đổi giữa các lần chạy.)

java.util.regex.Pattern object externals:
          ADDRESS       SIZE TYPE                             PATH                           VALUE
         d5e5f290         16 java.util.regex.Pattern$Node     .root.next.atom.next           (object)
         d5e5f2a0        120 (something else)                 (somewhere else)               (something else)
         d5e5f318         16 java.util.regex.Pattern$LastNode .root.next.next.next.next.next.next.next (object)
         d5e5f328      21664 (something else)                 (somewhere else)               (something else)
         d5e647c8         24 java.lang.String                 .pattern                       (object)
         d5e647e0        112 [C                               .pattern.value                 [^, [, a, -, z, A, -, Z, 0, -, 9, _, ., +, -, ], +, @, [, a, -, z, A, -, Z, 0, -, 9, -, ], +, \, ., [, a, -, z, A, -, Z, 0, -, 9, -, ., ], +, $]
         d5e64850        448 (something else)                 (somewhere else)               (something else)
         d5e64a10         72 java.util.regex.Pattern                                         (object)
         d5e64a58        416 (something else)                 (somewhere else)               (something else)
         d5e64bf8         16 java.util.regex.Pattern$Begin    .root                          (object)
         d5e64c08         24 java.util.regex.Pattern$BitClass .root.next.atom.val$rhs        (object)
         d5e64c20        272 [Z                               .root.next.atom.val$rhs.bits   [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
         d5e64d30         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e64d48         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs.val$rhs (object)
         d5e64d60         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e64d78         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$rhs (object)
         d5e64d90         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e64da8         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs (object)
         d5e64dc0         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs (object)
         d5e64dd8         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs        (object)
         d5e64df0         24 java.util.regex.Pattern$5        .root.next.atom                (object)
         d5e64e08         32 java.util.regex.Pattern$Curly    .root.next                     (object)
         d5e64e28         24 java.util.regex.Pattern$Single   .root.next.next                (object)
         d5e64e40         24 java.util.regex.Pattern$BitClass .root.next.next.next.atom.val$rhs (object)
         d5e64e58        272 [Z                               .root.next.next.next.atom.val$rhs.bits [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
         d5e64f68         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$lhs.val$lhs (object)
         d5e64f80         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$lhs.val$rhs (object)
         d5e64f98         24 java.util.regex.Pattern$5        .root.next.next.next.atom.val$lhs.val$lhs (object)
         d5e64fb0         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$rhs (object)
         d5e64fc8         24 java.util.regex.Pattern$5        .root.next.next.next.atom.val$lhs (object)
         d5e64fe0         24 java.util.regex.Pattern$5        .root.next.next.next.atom      (object)
         d5e64ff8         32 java.util.regex.Pattern$Curly    .root.next.next.next           (object)
         d5e65018         24 java.util.regex.Pattern$Single   .root.next.next.next.next      (object)
         d5e65030         24 java.util.regex.Pattern$BitClass .root.next.next.next.next.next.atom.val$rhs (object)
         d5e65048        272 [Z                               .root.next.next.next.next.next.atom.val$rhs.bits [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
         d5e65158         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e65170         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs.val$rhs (object)
         d5e65188         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs (object)
         d5e651a0         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$rhs (object)
         d5e651b8         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs.val$lhs (object)
         d5e651d0         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs (object)
         d5e651e8         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom (object)
         d5e65200         32 java.util.regex.Pattern$Curly    .root.next.next.next.next.next (object)
         d5e65220        120 (something else)                 (somewhere else)               (something else)
         d5e65298         24 java.util.regex.Pattern$Dollar   .root.next.next.next.next.next.next (object)

Các mục "(cái gì khác)" mô tả các đối tượng khác trong heap không phải là một phần của biểu đồ đối tượng này .

Tài liệu jol tốt nhất là các mẫu jol trong kho jol. Các mẫu thể hiện các hoạt động jol phổ biến và chỉ ra cách bạn có thể sử dụng jol để phân tích VM và các phần tử thu gom rác.


18
Câu trả lời này nên có nhiều upvote. Chắc chắn là một lựa chọn rất tốt để kiểm tra. EDIT: Đã kiểm tra rằng điều này đã được thêm vào năm nay trong khi câu hỏi đã được hỏi trong '08. Có lẽ là lựa chọn tốt nhất và dễ nhất để làm những gì OP yêu cầu vào lúc này.
giá thuê

4
Tác giả công cụ đã viết một bài blog về Jol .
Mike

2
Để xác định kích thước của Object "obj", hãy sử dụng: org.openjdk.jol.info.GraphLayout.parseInstance (obj) .totalSize ();
mạnh mẽ

Lưu ý rằng vmDetailsbây giờ VM.current().details().
Miha_x64

Kiểm tra GraphLayout.parseInstance(instance).toFootprint()Tôi thấy hữu ích hơn để hiểu kích thước đối tượng
Mugen

82

Tôi vô tình tìm thấy một lớp java "jdk.nashorn.iternal.ir.debug.ObjectSizeCalculator", đã có trong jdk, rất dễ sử dụng và có vẻ khá hữu ích để xác định kích thước của một đối tượng.

System.out.println(ObjectSizeCalculator.getObjectSize(new gnu.trove.map.hash.TObjectIntHashMap<String>(12000, 0.6f, -1)));
System.out.println(ObjectSizeCalculator.getObjectSize(new HashMap<String, Integer>(100000)));
System.out.println(ObjectSizeCalculator.getObjectSize(3));
System.out.println(ObjectSizeCalculator.getObjectSize(new int[]{1, 2, 3, 4, 5, 6, 7 }));
System.out.println(ObjectSizeCalculator.getObjectSize(new int[100]));

các kết quả:

164192
48
16
48
416

3
Tương tự ở đây, tôi đã thử các giải pháp khác được đề xuất ở trên và tình cờ thấy ObjectSizeCalculator. Tôi tin rằng không ai đề cập đến nếu trước đó kể từ khi nó được giới thiệu gần đây trên JDK 8 như một phần của dự án Nashorn . Tuy nhiên tôi không tìm thấy bất kỳ tài liệu chính thức nào về lớp học này trên web.
Henrique Gontijo

Nó dường như không xem xét độ dài chuỗi. Có phải chỉ là về kích thước trên ngăn xếp?
jontejj

1
Tôi có một hashmap, trong đó com.carrotsearch.RamUsageEstimator trả về khoảng một nửa ObjectSizeCalculator. Cái nào là đúng? - Cái nào đáng tin cậy hơn?
badera

9
Lưu ý rằng ObjectSizeCalculatorchỉ được hỗ trợ trên HotSpot VM
kellanburket

74

Vài năm trước, Javawworld đã có một bài viết về việc xác định kích thước của các đối tượng Java tổng hợp và có khả năng lồng nhau , về cơ bản họ sẽ thực hiện việc tạo ra một triển khai sizeof () trong Java. Cách tiếp cận về cơ bản dựa trên công việc khác, nơi mọi người xác định bằng thực nghiệm kích thước của các đối tượng nguyên thủy và các đối tượng Java điển hình và sau đó áp dụng kiến ​​thức đó vào một phương pháp đệ quy một biểu đồ đối tượng để kiểm tra tổng kích thước.

Nó luôn luôn có phần kém chính xác hơn so với triển khai C gốc đơn giản chỉ vì những điều xảy ra đằng sau hậu trường của một lớp nhưng nó phải là một chỉ báo tốt.

Ngoài ra, một dự án SourceForge được gọi một cách thích hợp là sizeof cung cấp thư viện Java5 với cách triển khai sizeof ().

PS Không sử dụng phương pháp tuần tự hóa, không có mối tương quan giữa kích thước của một đối tượng được tuần tự hóa và dung lượng bộ nhớ mà nó tiêu thụ khi còn sống.


6
Tiện ích sizeof có lẽ là cách nhanh nhất. Về cơ bản, đó là những gì Stefan nói, nhưng đã được đóng gói trong một cái lọ sẵn sàng để sử dụng.
Alexandre L kể

62

Thứ nhất "kích thước của một đối tượng" không phải là một khái niệm được xác định rõ trong Java. Bạn có thể có nghĩa là chính đối tượng, chỉ với các thành viên của nó, Đối tượng và tất cả các đối tượng mà nó đề cập đến (biểu đồ tham chiếu). Bạn có thể có nghĩa là kích thước trong bộ nhớ hoặc kích thước trên đĩa. Và JVM được phép tối ưu hóa những thứ như Chuỗi.

Vì vậy, cách duy nhất đúng là hỏi JVM, với một trình lược tả tốt (tôi sử dụng YourKit ), đây có thể không phải là điều bạn muốn.

Tuy nhiên, từ mô tả ở trên, có vẻ như mỗi hàng sẽ được khép kín và không có cây phụ thuộc lớn, vì vậy phương thức tuần tự hóa có thể sẽ là một xấp xỉ tốt trên hầu hết các JVM. Cách dễ nhất để làm điều này là như sau:

 Serializable ser;
 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 ObjectOutputStream oos = new ObjectOutputStream(baos);
 oos.writeObject(ser);
 oos.close();
 return baos.size();

Hãy nhớ rằng nếu bạn có các đối tượng với các tham chiếu chung thì điều này sẽ không cho kết quả chính xác và kích thước của tuần tự hóa sẽ không luôn luôn khớp với kích thước trong bộ nhớ, nhưng đó là một xấp xỉ tốt. Mã sẽ hiệu quả hơn một chút nếu bạn khởi tạo kích thước ByteArrayOutputStream thành một giá trị hợp lý.


2
Tôi thích cách tiếp cận này. Làm thế nào xa về kích thước đối tượng bạn đã được tắt.
Berlin Brown

1
Rất đơn giản và hiệu quả. Các phương thức khác quá lộn xộn (đặc biệt bên trong RCP Eclipse). Cảm ơn.
marcolopes

19
Tuần tự hóa sẽ không theo dõi các biến tạm thời và phương thức tuần tự hóa mặc định ghi các chuỗi trong UTF-8, do đó, bất kỳ ký tự ANSI nào cũng sẽ chỉ mất một byte. Nếu bạn có nhiều chuỗi, kích thước của bạn sẽ trở nên vô dụng.
TMN

1
Mặc dù điều này có thể không cho kích thước chính xác, nhưng đối với nhu cầu của tôi, tôi chỉ cần so sánh giữa 2 đối tượng và SizeOf sẽ không khởi tạo từ một ứng dụng web. Cảm ơn!
Isaac

1
Đề xuất tốt của YourKit . Các lựa chọn thay thế khác là VirtualVMjvmmonitor
angelcervera

38

Nếu bạn muốn biết bao nhiêu bộ nhớ đang được sử dụng trong JVM của bạn và bao nhiêu là miễn phí, bạn có thể thử một cái gì đó như thế này:

// Get current size of heap in bytes
long heapSize = Runtime.getRuntime().totalMemory();

// Get maximum size of heap in bytes. The heap cannot grow beyond this size.
// Any attempt will result in an OutOfMemoryException.
long heapMaxSize = Runtime.getRuntime().maxMemory();

// Get amount of free memory within the heap in bytes. This size will increase
// after garbage collection and decrease as new objects are created.
long heapFreeSize = Runtime.getRuntime().freeMemory();

chỉnh sửa: Tôi nghĩ rằng điều này có thể hữu ích vì tác giả câu hỏi cũng nói rằng anh ta muốn có logic xử lý "đọc càng nhiều hàng càng tốt cho đến khi tôi sử dụng 32 MB bộ nhớ."


24
Đây không phải là một giải pháp tốt, vì bạn không bao giờ biết khi nào việc thu gom rác sẽ xảy ra, hoặc bao nhiêu bộ nhớ bổ sung sẽ được phân bổ cho heap cùng một lúc.
Nick FortesTHER

5
Đó là sự thật, và tôi sẽ không có ý định này để giải quyết câu hỏi chính của bài đăng này, nhưng nó có thể giúp anh ta biết lập trình khi anh ta đang tiến gần đến mức đạt được kích thước heap tối đa.
matt b

1
Vấn đề khác của giải pháp này là khi bạn ở trong môi trường đa luồng (như trong máy chủ web). Có thể các luồng khác đã được thực thi và tiêu thụ bộ nhớ. Với phép tính gần đúng này, bạn đang tính toán bộ nhớ đã sử dụng trong tất cả các máy ảo.
angelcervera

1
Một nhược điểm khác là freeMemory trả về một xấp xỉ. Hãy thử tạo một đối tượng javax.crypto.Codes. Sự khác biệt giữa hai lệnh gọi freeMemory (để ước tính kích thước của Mật mã) là không đổi!
Eugen

1
Tôi tin rằng bạn có thể buộc một bộ sưu tập rác, vì vậy bạn có thể làm một số thứ trong phương pháp này.
matanster

24

Quay lại khi tôi làm việc tại Twitter, tôi đã viết một tiện ích để tính kích thước đối tượng sâu. Nó tính đến các mô hình bộ nhớ khác nhau (32 bit, oops nén, 64 bit), phần đệm, phần đệm lớp con, hoạt động chính xác trên các cấu trúc dữ liệu tròn và mảng. Bạn chỉ có thể biên dịch tập tin .java này; nó không có phụ thuộc bên ngoài:

https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/objectsize/ObjectSizeCalculator.java


1
Szia! Tôi cũng muốn hét lên bài thuyết trình của bạn : slide 15-20 thật tuyệt để giúp có được cảm giác bản năng về chi phí của các quyết định cấu trúc dữ liệu khác nhau. Cảm ơn đã đăng bài đó!
Luke Usherwood

16
"Nó không có phụ thuộc bên ngoài" - từ khi nào ổi không phải là phụ thuộc bên ngoài?
l4mpi


Guave là một phụ thuộc bên ngoài.
Mert Serimer

18

Phần lớn các câu trả lời khác cung cấp kích thước nông - ví dụ: kích thước của HashMap mà không có bất kỳ khóa hoặc giá trị nào, không có khả năng là những gì bạn muốn.

Dự án jamm sử dụng gói java.lang.instrumentation ở trên nhưng đi trên cây và do đó có thể cung cấp cho bạn sử dụng bộ nhớ sâu.

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm

Để sử dụng MemoryMeter, hãy khởi động JVM bằng "-javaagent: /jamm.jar"


11

Bạn phải đi bộ các đối tượng bằng cách sử dụng sự phản chiếu. Hãy cẩn thận khi bạn làm:

  • Chỉ cần phân bổ một đối tượng có một số chi phí trong JVM. Số lượng thay đổi theo JVM để bạn có thể biến giá trị này thành tham số. Ít nhất làm cho nó là một hằng số (8 byte?) Và áp dụng cho mọi thứ được phân bổ.
  • Về bytelý thuyết chỉ là 1 byte không có nghĩa là nó chỉ mất một bộ nhớ.
  • Sẽ có các vòng lặp trong các tham chiếu đối tượng, vì vậy bạn sẽ cần giữ một HashMaphoặc somesuch bằng cách sử dụng các đối tượng bằng như bộ so sánh để loại bỏ các vòng lặp vô hạn.

@jodonnell: Tôi thích sự đơn giản của giải pháp của bạn, nhưng nhiều đối tượng không phải là Tuần tự hóa (vì vậy điều này sẽ tạo ra một ngoại lệ), các trường có thể là nhất thời và các đối tượng có thể ghi đè các phương thức chuẩn.


Không phải kích thước của các nguyên thủy khác nhau được định nghĩa trong Đặc tả Java? (§2.4.1)
erickson

4
Không phải theo nghĩa "nó chiếm bao nhiêu bộ nhớ", đó là câu hỏi. Chỉ trong ý nghĩa của cách họ hoạt động. Ví dụ: byte, ký tự và quần short chiếm toàn bộ một từ trên ngăn xếp Java, mặc dù chúng hoạt động với làm tròn, v.v.
Jason Cohen

1
Điều này nghe có vẻ tương tự như đo kích thước, như được hiển thị bởi Heinz trong Bản tin số 78: javaspecialists.eu/archive/Issue078.html . Tôi đã sử dụng nó. Cách tiếp cận của ông hoạt động.
Peter Kofler

8

Bạn phải đo nó bằng một công cụ hoặc ước tính nó bằng tay và nó phụ thuộc vào JVM mà bạn đang sử dụng.

Có một số chi phí cố định cho mỗi đối tượng. Nó đặc trưng cho JVM, nhưng tôi thường ước tính 40 byte. Sau đó, bạn phải nhìn vào các thành viên của lớp. Tham chiếu đối tượng là 4 (8) byte trong JVM 32 bit (64 bit). Các loại nguyên thủy là:

  • boolean và byte: 1 byte
  • char và ngắn: 2 byte
  • int và float: 4 byte
  • dài và gấp đôi: 8 byte

Mảng tuân theo các quy tắc tương tự; nghĩa là, đó là một tham chiếu đối tượng để lấy 4 (hoặc 8) byte trong đối tượng của bạn và sau đó độ dài của nó nhân với kích thước của phần tử của nó.

Cố gắng thực hiện theo chương trình với các lệnh gọi để Runtime.freeMemory()không mang lại cho bạn độ chính xác cao, vì các cuộc gọi không đồng bộ đến trình thu gom rác, v.v. Cấu hình heap bằng -Xrunhprof hoặc các công cụ khác sẽ cho bạn kết quả chính xác nhất.


@erickson Tôi không chắc chắn về sizeof (boolean) == 1 khi xem chủ đề này ( stackoverflow.com/questions/1907318/ trộm ). Bạn có thể vui lòng bình luận về điều này?
dma_k

2
@dma_k, Java thực sự không có booleans thực sự. Kích thước của boolean là 4byte bên ngoài mảng và 1byte bên trong boolean[]. Trên thực tế tất cả các kiểu nguyên thủy không gấp đôi / dài là 4 byte. Cái sau là 8 (câu trả lời sai đặt chúng là 4)
bestsss

@bestsss: Để chính xác hơn, việc cấp phát bộ nhớ tối thiểu phụ thuộc vào nền tảng và việc triển khai JVM. Ngoài ra các đối tượng trên heap được căn chỉnh, vì vậy sau khi tổng hợp tất cả các kích thước, người ta cần làm tròn lên.
dma_k

6

Các java.lang.instrument.Instrumentationlớp học cung cấp một cách tốt đẹp để có được kích thước của một đối tượng Java, nhưng nó đòi hỏi bạn phải xác định một premainvà chạy chương trình của bạn với một đại lý java. Điều này rất nhàm chán khi bạn không cần bất kỳ tác nhân nào và sau đó bạn phải cung cấp một tác nhân Jar giả cho ứng dụng của mình.

Vì vậy, tôi đã có một giải pháp thay thế bằng cách sử dụng Unsafelớp từ sun.misc. Vì vậy, xem xét việc căn chỉnh heap đối tượng theo kiến ​​trúc bộ xử lý và tính toán bù trường tối đa, bạn có thể đo kích thước của Đối tượng Java. Trong ví dụ dưới đây, tôi sử dụng một lớp phụ trợ UtilUnsafeđể có được một tham chiếu đến sun.misc.Unsafeđối tượng.

private static final int NR_BITS = Integer.valueOf(System.getProperty("sun.arch.data.model"));
private static final int BYTE = 8;
private static final int WORD = NR_BITS/BYTE;
private static final int MIN_SIZE = 16; 

public static int sizeOf(Class src){
    //
    // Get the instance fields of src class
    // 
    List<Field> instanceFields = new LinkedList<Field>();
    do{
        if(src == Object.class) return MIN_SIZE;
        for (Field f : src.getDeclaredFields()) {
            if((f.getModifiers() & Modifier.STATIC) == 0){
                instanceFields.add(f);
            }
        }
        src = src.getSuperclass();
    }while(instanceFields.isEmpty());
    //
    // Get the field with the maximum offset
    //  
    long maxOffset = 0;
    for (Field f : instanceFields) {
        long offset = UtilUnsafe.UNSAFE.objectFieldOffset(f);
        if(offset > maxOffset) maxOffset = offset; 
    }
    return  (((int)maxOffset/WORD) + 1)*WORD; 
}
class UtilUnsafe {
    public static final sun.misc.Unsafe UNSAFE;

    static {
        Object theUnsafe = null;
        Exception exception = null;
        try {
            Class<?> uc = Class.forName("sun.misc.Unsafe");
            Field f = uc.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            theUnsafe = f.get(uc);
        } catch (Exception e) { exception = e; }
        UNSAFE = (sun.misc.Unsafe) theUnsafe;
        if (UNSAFE == null) throw new Error("Could not obtain access to sun.misc.Unsafe", exception);
    }
    private UtilUnsafe() { }
}

Cách tiếp cận thú vị, nhưng điều này không cho rằng đối tượng và lưu trữ trường của nó không bị phân mảnh?
nicoulaj

Có và tôi không biết bất kỳ triển khai JVM nào tạo ra sự phân mảnh như vậy.
Miguel Gamboa

Tôi không hiểu Phân mảnh không phải là một lựa chọn :) Chúng ta hãy lấy ví dụ về đối tượng C được lưu trữ dưới dạng trường của các đối tượng A và B. Không phải nó sẽ dịch chuyển toàn bộ vật trong A hoặc B?
nicoulaj

Xin lỗi, nhưng tôi không hiểu quan điểm của bạn. Theo cách giải thích của tôi, trong các đối tượng Java không thể được lưu trữ bên trong các đối tượng khác, như xảy ra với cấu trúc C hoặc Loại giá trị trong .Net. Vì vậy, khi bạn nói: đối tượng C được lưu trữ dưới dạng trường của các đối tượng A và B, có nghĩa là các đối tượng A và B có các trường lưu trữ các tham chiếu (con trỏ) đến đối tượng C. Khi đó kích thước của A và B bằng phần bù của trường đó cộng với kích thước của một tham chiếu (con trỏ) đến đối tượng C. Và kích thước của một tham chiếu là kích thước của một từ.
Miguel Gamboa

Oh, OK, chúng ta đang nói về kích thước nông. Lỗi của tôi.
nicoulaj

6

Ngoài ra còn có công cụ Bộ đo Bộ nhớ (trước đây là Google Code , giờ là trên GitHub ), đơn giản và được xuất bản theo giấy phép Apache 2.0 thân thiện với thương mại , như đã thảo luận trong một câu hỏi tương tự .

Nó cũng yêu cầu một đối số dòng lệnh cho trình thông dịch java nếu bạn muốn đo mức tiêu thụ byte bộ nhớ, nhưng nếu không thì dường như chỉ hoạt động tốt, ít nhất là trong các kịch bản tôi đã sử dụng nó.


4

Không cần phải lộn xộn với thiết bị, v.v. và nếu bạn không cần biết kích thước chính xác byte của một đối tượng, bạn có thể thực hiện theo cách tiếp cận sau:

System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

do your job here

System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

Bằng cách này, bạn đọc bộ nhớ đã sử dụng trước và sau, và gọi cho GC ngay trước khi nhận được bộ nhớ đã sử dụng, bạn giảm "nhiễu" xuống gần bằng 0.

Để có kết quả đáng tin cậy hơn, bạn có thể chạy công việc của mình n lần, sau đó chia bộ nhớ đã sử dụng cho n, thu được bao nhiêu bộ nhớ mà một lần chạy. Thậm chí nhiều hơn, bạn có thể chạy toàn bộ nhiều lần hơn và kiếm trung bình.


5
Không System.gc()chỉ thông báo rằng bạn muốn GC? Nó không được đảm bảo rằng GC được gọi ở tất cả.
Raildex

@reallynice. Điều này không an toàn vì bạn có thể không bao giờ làm những gì GC làm hoặc ảnh hưởng đến bộ nhớ giữa các dòng của bạn. Vì vậy, "giữa" hai phương thức freeMemory, GC có thể giải phóng thêm không gian mà bạn không xem xét, do đó, đối tượng của bạn sẽ trông nhỏ hơn
Mert Serimer

@MertSerimer "không an toàn" đối với tôi ở tất cả các cấp độ khác nhau: nhiều nhất điều này không chính xác lắm, như tôi cũng đã nói. Ngoài ra, bạn không thể lái xe GC (như Raildex đã nêu), nhưng trong trường hợp này tôi cũng đề nghị chèn cái này vào một chu kỳ. Đây chỉ là một hệ thống nhanh chóng và bẩn thỉu và gần đúng, hoạt động nếu kết quả không cần phải rất đáng tin cậy, như đã nêu.
thực sự là

Có rất nhiều vấn đề với điều này nhưng nó mang lại cho bạn một swag tốt.
markthegrea

3

Đây là một tiện ích tôi đã tạo bằng cách sử dụng một số ví dụ được liên kết để xử lý 32 bit, 64 bit và 64 bit với OOP được nén. Nó sử dụng sun.misc.Unsafe.

Nó sử dụng Unsafe.addressSize()để có được kích thước của một con trỏ riêng và Unsafe.arrayIndexScale( Object[].class )cho kích thước của một tham chiếu Java.

Nó sử dụng phần bù trường của một lớp đã biết để tính ra kích thước cơ sở của một đối tượng.

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.IdentityHashMap;
import java.util.Stack;
import sun.misc.Unsafe;

/** Usage: 
 * MemoryUtil.sizeOf( object )
 * MemoryUtil.deepSizeOf( object )
 * MemoryUtil.ADDRESS_MODE
 */
public class MemoryUtil
{
    private MemoryUtil()
    {
    }

    public static enum AddressMode
    {
        /** Unknown address mode. Size calculations may be unreliable. */
        UNKNOWN,
        /** 32-bit address mode using 32-bit references. */
        MEM_32BIT,
        /** 64-bit address mode using 64-bit references. */
        MEM_64BIT,
        /** 64-bit address mode using 32-bit compressed references. */
        MEM_64BIT_COMPRESSED_OOPS
    }

    /** The detected runtime address mode. */
    public static final AddressMode ADDRESS_MODE;

    private static final Unsafe UNSAFE;

    private static final long ADDRESS_SIZE; // The size in bytes of a native pointer: 4 for 32 bit, 8 for 64 bit
    private static final long REFERENCE_SIZE; // The size of a Java reference: 4 for 32 bit, 4 for 64 bit compressed oops, 8 for 64 bit
    private static final long OBJECT_BASE_SIZE; // The minimum size of an Object: 8 for 32 bit, 12 for 64 bit compressed oops, 16 for 64 bit
    private static final long OBJECT_ALIGNMENT = 8;

    /** Use the offset of a known field to determine the minimum size of an object. */
    private static final Object HELPER_OBJECT = new Object() { byte b; };


    static
    {
        try
        {
            // Use reflection to get a reference to the 'Unsafe' object.
            Field f = Unsafe.class.getDeclaredField( "theUnsafe" );
            f.setAccessible( true );
            UNSAFE = (Unsafe) f.get( null );

            OBJECT_BASE_SIZE = UNSAFE.objectFieldOffset( HELPER_OBJECT.getClass().getDeclaredField( "b" ) );

            ADDRESS_SIZE = UNSAFE.addressSize();
            REFERENCE_SIZE = UNSAFE.arrayIndexScale( Object[].class );

            if( ADDRESS_SIZE == 4 )
            {
                ADDRESS_MODE = AddressMode.MEM_32BIT;
            }
            else if( ADDRESS_SIZE == 8 && REFERENCE_SIZE == 8 )
            {
                ADDRESS_MODE = AddressMode.MEM_64BIT;
            }
            else if( ADDRESS_SIZE == 8 && REFERENCE_SIZE == 4 )
            {
                ADDRESS_MODE = AddressMode.MEM_64BIT_COMPRESSED_OOPS;
            }
            else
            {
                ADDRESS_MODE = AddressMode.UNKNOWN;
            }
        }
        catch( Exception e )
        {
            throw new Error( e );
        }
    }


    /** Return the size of the object excluding any referenced objects. */
    public static long shallowSizeOf( final Object object )
    {
        Class<?> objectClass = object.getClass();
        if( objectClass.isArray() )
        {
            // Array size is base offset + length * element size
            long size = UNSAFE.arrayBaseOffset( objectClass )
                    + UNSAFE.arrayIndexScale( objectClass ) * Array.getLength( object );
            return padSize( size );
        }
        else
        {
            // Object size is the largest field offset padded out to 8 bytes
            long size = OBJECT_BASE_SIZE;
            do
            {
                for( Field field : objectClass.getDeclaredFields() )
                {
                    if( (field.getModifiers() & Modifier.STATIC) == 0 )
                    {
                        long offset = UNSAFE.objectFieldOffset( field );
                        if( offset >= size )
                        {
                            size = offset + 1; // Field size is between 1 and PAD_SIZE bytes. Padding will round up to padding size.
                        }
                    }
                }
                objectClass = objectClass.getSuperclass();
            }
            while( objectClass != null );

            return padSize( size );
        }
    }


    private static final long padSize( final long size )
    {
        return (size + (OBJECT_ALIGNMENT - 1)) & ~(OBJECT_ALIGNMENT - 1);
    }


    /** Return the size of the object including any referenced objects. */
    public static long deepSizeOf( final Object object )
    {
        IdentityHashMap<Object,Object> visited = new IdentityHashMap<Object,Object>();
        Stack<Object> stack = new Stack<Object>();
        if( object != null ) stack.push( object );

        long size = 0;
        while( !stack.isEmpty() )
        {
            size += internalSizeOf( stack.pop(), stack, visited );
        }
        return size;
    }


    private static long internalSizeOf( final Object object, final Stack<Object> stack, final IdentityHashMap<Object,Object> visited )
    {
        // Scan for object references and add to stack
        Class<?> c = object.getClass();
        if( c.isArray() && !c.getComponentType().isPrimitive() )
        {
            // Add unseen array elements to stack
            for( int i = Array.getLength( object ) - 1; i >= 0; i-- )
            {
                Object val = Array.get( object, i );
                if( val != null && visited.put( val, val ) == null )
                {
                    stack.add( val );
                }
            }
        }
        else
        {
            // Add unseen object references to the stack
            for( ; c != null; c = c.getSuperclass() )
            {
                for( Field field : c.getDeclaredFields() )
                {
                    if( (field.getModifiers() & Modifier.STATIC) == 0 
                            && !field.getType().isPrimitive() )
                    {
                        field.setAccessible( true );
                        try
                        {
                            Object val = field.get( object );
                            if( val != null && visited.put( val, val ) == null )
                            {
                                stack.add( val );
                            }
                        }
                        catch( IllegalArgumentException e )
                        {
                            throw new RuntimeException( e );
                        }
                        catch( IllegalAccessException e )
                        {
                            throw new RuntimeException( e );
                        }
                    }
                }
            }
        }

        return shallowSizeOf( object );
    }
}

Bạn đã kiểm tra lớp này với các giá trị? Tôi đã thử, nhưng đối với tôi, giá trị không chính xác !!!
Débora

1
Các giá trị mà nó mang lại cho tôi cho một đối tượng đơn giản là chính xác, nhưng giảm đi 10 lần cho một danh sách chứa các đối tượng 1mio. Tuy nhiên, công việc rất tốt đẹp!
Michael Böckling

Hấp dẫn. Tôi đã thử nghiệm nó bằng cách sử dụng JDK7u67, trên Windows 7 x64 và Linux 2.6.16 / x86_64, sử dụng từng chế độ địa chỉ 32bit / 64bit / oop. Tôi đã so sánh nó với các bãi chứa bộ nhớ được phân tích trong Trình phân tích bộ nhớ Eclipse 1.3.x. Bạn đang sử dụng thiết lập nào? Bạn có một ví dụ cụ thể tôi có thể thử?
dlaudams

Lựa chọn tốt nhất tôi có thể làm. Tôi không thể sử dụng Instrumentationvì tôi không bắt đầu tomcat, ObjectSizeCalculatorvì không chắc chắn về loại VM (HotSpot) và JOLđậu mùa xuân. Tôi sử dụng điều này và thêm tham số thứ hai để bỏ qua mã singletons viz AbstractRefreshableApplicationContext.getBeanFactory().getSingletonMutex()internalSizeOfmã tái cấu trúc để bỏ qua Class và Enum
Perlos

Để so sánh kết quả, hãy sử dụng ObjectSizeCalculator (Tính toàn bộ máy chủ 1GB đến 10 giây). JOL gây ra MemError (6GB không được kích hoạt) và tôi không nhận được kết quả tương tự, có thể là do enum.
Perlos

3

Tôi đang tìm kiếm một tính toán thời gian chạy của một kích thước đối tượng đáp ứng các yêu cầu sau:

  • Có sẵn trong thời gian chạy mà không cần bao gồm các thiết bị.
  • Hoạt động với Java 9+ mà không cần truy cập vào Không an toàn.
  • Chỉ dựa trên Class. Không phải là một sizeOf sâu có tính đến độ dài chuỗi, độ dài mảng, v.v.

Phần sau đây dựa trên mã cốt lõi của bài viết chuyên gia java gốc ( https://www.javaspecialists.eu/archive/Issue078.html ) và một vài bit từ phiên bản Không an toàn trong câu trả lời khác cho câu hỏi này.

Tôi hy vọng ai đó thấy nó hữu ích.

public class JavaSize {

private static final int NR_BITS = Integer.valueOf(System.getProperty("sun.arch.data.model"));
private static final int BYTE = 8;
private static final int WORD = NR_BITS / BYTE;
private static final int HEADER_SIZE = 8;

public static int sizeOf(Class<?> clazz) {
    int result = 0;

    while (clazz != null) {
        Field[] fields = clazz.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            if (!Modifier.isStatic(fields[i].getModifiers())) {
                if (fields[i].getType().isPrimitive()) {
                    Class<?> primitiveClass = fields[i].getType();
                    if (primitiveClass == boolean.class || primitiveClass == byte.class) {
                        result += 1;
                    } else if (primitiveClass == short.class) {
                        result += 2;
                    } else if (primitiveClass == int.class || primitiveClass == float.class) {
                        result += 4;
                    } else if (primitiveClass == double.class || primitiveClass == long.class) {
                        result += 8;
                    }

                } else {
                    // assume compressed references.
                    result += 4;
                }
            }
        }

        clazz = clazz.getSuperclass();

        // round up to the nearest WORD length.
        if ((result % WORD) != 0) {
            result += WORD - (result % WORD);
        }
    }

    result += HEADER_SIZE;

    return result;
}

}


2

Không có cuộc gọi phương thức, nếu đó là những gì bạn đang yêu cầu. Với một ít nghiên cứu, tôi cho rằng bạn có thể tự viết. Một thể hiện cụ thể có kích thước cố định xuất phát từ số lượng tài liệu tham khảo và giá trị nguyên thủy cộng với dữ liệu sổ sách cá thể. Bạn chỉ đơn giản là đi bộ đồ thị đối tượng. Các loại hàng càng ít thay đổi, càng dễ dàng.

Nếu điều đó quá chậm hoặc chỉ là rắc rối nhiều hơn giá trị của nó, thì luôn luôn có hàng lỗi thời lỗi thời.


2

Tôi đã viết một bài kiểm tra nhanh một lần để ước tính khi đang bay:

public class Test1 {

    // non-static nested
    class Nested { }

    // static nested
    static class StaticNested { }

    static long getFreeMemory () {
        // waits for free memory measurement to stabilize
        long init = Runtime.getRuntime().freeMemory(), init2;
        int count = 0;
        do {
            System.out.println("waiting..." + init);
            System.gc();
            try { Thread.sleep(250); } catch (Exception x) { }
            init2 = init;
            init = Runtime.getRuntime().freeMemory();
            if (init == init2) ++ count; else count = 0;
        } while (count < 5);
        System.out.println("ok..." + init);
        return init;
    }

    Test1 () throws InterruptedException {

        Object[] s = new Object[10000];
        Object[] n = new Object[10000];
        Object[] t = new Object[10000];

        long init = getFreeMemory();

        //for (int j = 0; j < 10000; ++ j)
        //    s[j] = new Separate();

        long afters = getFreeMemory();

        for (int j = 0; j < 10000; ++ j)
            n[j] = new Nested();

        long aftersn = getFreeMemory();

        for (int j = 0; j < 10000; ++ j)
            t[j] = new StaticNested();

        long aftersnt = getFreeMemory();

        System.out.println("separate:      " + -(afters - init) + " each=" + -(afters - init) / 10000);
        System.out.println("nested:        " + -(aftersn - afters) + " each=" + -(aftersn - afters) / 10000);
        System.out.println("static nested: " + -(aftersnt - aftersn) + " each=" + -(aftersnt - aftersn) / 10000);

    }

    public static void main (String[] args) throws InterruptedException {
        new Test1();
    }

}

Khái niệm chung là phân bổ các đối tượng và đo lường sự thay đổi trong không gian heap miễn phí. Khóa chính getFreeMemory(), yêu cầu GC chạy và chờ kích thước heap miễn phí được báo cáo để ổn định . Đầu ra của ở trên là:

nested:        160000 each=16
static nested: 160000 each=16

Đó là những gì chúng ta mong đợi, đưa ra hành vi căn chỉnh và chi phí tiêu đề khối heap có thể.

Phương pháp thiết bị chi tiết trong câu trả lời được chấp nhận ở đây chính xác nhất. Phương pháp tôi mô tả là chính xác nhưng chỉ trong các điều kiện được kiểm soát trong đó không có luồng nào khác đang tạo / loại bỏ các đối tượng.


2

Chỉ cần sử dụng java trực quan VM.

Nó có mọi thứ bạn cần để cấu hình và gỡ lỗi các vấn đề bộ nhớ.

Nó cũng có bảng điều khiển OQL (Ngôn ngữ truy vấn đối tượng) cho phép bạn thực hiện nhiều việc hữu ích, một trong số đó là sizeof(o)


2

Khi sử dụng JetBrains IntelliJ, trước tiên hãy bật "Đính kèm tác nhân bộ nhớ" trong Tệp | Cài đặt | Xây dựng, Thi công, Triển khai | Trình gỡ lỗi.

Khi gỡ lỗi, nhấp chuột phải vào một biến quan tâm và chọn "Tính kích thước giữ lại": Tính kích thước giữ lại


1

Câu trả lời của tôi dựa trên mã được cung cấp bởi Nick. Mã đó đo tổng số byte được chiếm bởi đối tượng được tuần tự hóa. Vì vậy, điều này thực sự đo các công cụ tuần tự hóa + dấu chân bộ nhớ đối tượng đơn giản (ví dụ chỉ là tuần tự hóa intvà bạn sẽ thấy tổng số byte được tuần tự hóa là không 4). Vì vậy, nếu bạn muốn lấy số byte thô được sử dụng chính xác cho đối tượng của mình - bạn cần sửa đổi mã đó một chút. Thích như vậy:

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class ObjectSizeCalculator {
    private Object getFirstObjectReference(Object o) {
        String objectType = o.getClass().getTypeName();

        if (objectType.substring(objectType.length()-2).equals("[]")) {
            try {
                if (objectType.equals("java.lang.Object[]"))
                    return ((Object[])o)[0];
                else if (objectType.equals("int[]"))
                    return ((int[])o)[0];
                else
                    throw new RuntimeException("Not Implemented !");
            } catch (IndexOutOfBoundsException e) {
                return null;
            }
        }

        return o;
    } 

    public int getObjectSizeInBytes(Object o) {
        final String STRING_JAVA_TYPE_NAME = "java.lang.String";

        if (o == null)
            return 0;

        String objectType = o.getClass().getTypeName();
        boolean isArray = objectType.substring(objectType.length()-2).equals("[]");

        Object objRef = getFirstObjectReference(o);
        if (objRef != null && !(objRef instanceof Serializable))
            throw new RuntimeException("Object must be serializable for measuring it's memory footprint using this method !");

        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(o);
            oos.close();
            byte[] bytes = baos.toByteArray();

            for (int i = bytes.length - 1, j = 0; i != 0; i--, j++) {
                if (objectType != STRING_JAVA_TYPE_NAME) {
                    if (bytes[i] == 112)
                        if (isArray)
                            return j - 4;
                        else
                            return j;
                } else {
                    if (bytes[i] == 0)
                        return j - 1;
                }
            }
        } catch (Exception e) {
            return -1;
        }

        return -1;
    }    

}

Tôi đã thử nghiệm giải pháp này với các kiểu nguyên thủy, Chuỗi và trên một số lớp tầm thường. Có thể không có trường hợp cũng được bảo hiểm.


CẬP NHẬT: Ví dụ được sửa đổi để hỗ trợ tính toán dấu chân bộ nhớ của các đối tượng mảng.


0

Bạn có thể tạo một đống heap (ví dụ với jmap) và sau đó phân tích đầu ra để tìm kích thước đối tượng. Đây là một giải pháp ngoại tuyến, nhưng bạn có thể kiểm tra kích thước nông và sâu, v.v.


0
long heapSizeBefore = Runtime.getRuntime().totalMemory();

// Code for object construction
...
long heapSizeAfter = Runtime.getRuntime().totalMemory();
long size = heapSizeAfter - heapSizeBefore;

kích thước cung cấp cho bạn sự gia tăng sử dụng bộ nhớ của jvm do tạo đối tượng và đó thường là kích thước của đối tượng.


Điều gì xảy ra nếu GC chạy ở giữa trong // Mã để xây dựng đối tượng? Bây giờ có thể mang lại kết quả chính xác tất cả các thời gian.
rajugaadu

0

Câu trả lời này không liên quan đến kích thước Đối tượng, nhưng khi bạn đang sử dụng mảng để chứa các đối tượng; dung lượng bộ nhớ sẽ phân bổ cho đối tượng.

Vì vậy, mảng, danh sách hoặc ánh xạ tất cả các bộ sưu tập đó sẽ không lưu trữ các đối tượng thực sự (chỉ tại thời điểm nguyên thủy, kích thước bộ nhớ đối tượng thực là cần thiết), nó sẽ chỉ lưu trữ các tham chiếu cho các đối tượng đó.

Bây giờ Used heap memory = sizeOfObj + sizeOfRef (* 4 bytes) in collection

  • (4/8 byte) phụ thuộc vào (32/64 bit) HĐH

VẤN ĐỀ

int   [] intArray    = new int   [1]; will require 4 bytes.
long  [] longArray   = new long  [1]; will require 8 bytes.

CÁC ĐỐI TƯỢNG

Object[] objectArray = new Object[1]; will require 4 bytes. The object can be any user defined Object.
Long  [] longArray   = new Long  [1]; will require 4 bytes.

Ý tôi là nói tất cả các đối tượng THAM KHẢO chỉ cần 4 byte bộ nhớ. Nó có thể là tham chiếu Chuỗi HOẶC Tham chiếu đối tượng kép, nhưng tùy thuộc vào việc tạo đối tượng, bộ nhớ cần thiết sẽ thay đổi.

ví dụ) Nếu tôi tạo đối tượng cho lớp bên dưới ReferenceMemoryTestthì 4 + 4 + 4 = 12 byte bộ nhớ sẽ được tạo. Bộ nhớ có thể khác nhau khi bạn đang cố gắng khởi tạo các tài liệu tham khảo.

 class ReferenceMemoryTest {
    public String refStr;
    public Object refObj;
    public Double refDoub; 
}

Vì vậy, khi tạo mảng đối tượng / tham chiếu, tất cả nội dung của nó sẽ bị chiếm dụng với các tham chiếu NULL. Và chúng tôi biết mỗi tham chiếu cần 4 byte.

Và cuối cùng, cấp phát bộ nhớ cho mã dưới đây là 20 byte.

ReferenceMemoryTest ref1 = new ReferenceMemoryTest (); (4 (ref1) + 12 = 16 byte) ReferenceMemoryTest ref2 = ref1; (4 (ref2) + 16 = 20 byte)


1
Làm thế nào một số nguyên 4 byte và một tham chiếu đối tượng có kích thước không xác định có thể khớp với 4 byte?
Hầu tước Lorne

@EJP Ý tôi là nói tất cả các đối tượng THAM KHẢO chỉ cần 4 byte bộ nhớ. Nó có thể là tham chiếu Chuỗi HOẶC Tham chiếu đối tượng kép, nhưng tùy thuộc vào việc tạo đối tượng, bộ nhớ cần thiết sẽ thay đổi.
Kanagavelu Sugumar

0

Giả sử tôi khai báo một lớp có tên Complexnhư:

public class Complex {

    private final long real;
    private final long imaginary;

    // omitted
}

Để xem có bao nhiêu bộ nhớ được phân bổ cho các thể hiện trực tiếp của lớp này:

$ jmap -histo:live <pid> | grep Complex

 num     #instances         #bytes  class name (module)
-------------------------------------------------------
 327:             1             32  Complex

-5

Đối với JSONObject, đoạn mã dưới đây có thể giúp bạn.

`JSONObject.toString().getBytes("UTF-8").length`

trả về kích thước tính theo byte

Tôi đã kiểm tra nó với đối tượng JSONArray của mình bằng cách ghi nó vào một tệp. Đó là cho kích thước đối tượng.


điều này sẽ chỉ làm việc cho các đối tượng chủ yếu là chuỗi.
Dexter Legaspi

-6

Tôi nghi ngờ bạn muốn làm điều đó theo chương trình trừ khi bạn chỉ muốn làm một lần và lưu trữ để sử dụng trong tương lai. Đó là một điều tốn kém để làm. Không có toán tử sizeof () trong Java và thậm chí nếu có, nó sẽ chỉ tính chi phí của các tham chiếu đến các đối tượng khác và kích thước của các nguyên hàm.

Một cách bạn có thể làm là tuần tự hóa thứ đó thành Tệp và xem kích thước của tệp, như sau:

Serializable myObject;
ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("obj.ser"));
oos.write (myObject);
oos.close ();

Tất nhiên, điều này giả định rằng mỗi đối tượng là khác biệt và không chứa các tham chiếu không nhất thời cho bất kỳ điều gì khác.

Một chiến lược khác là lấy từng đối tượng và kiểm tra các thành viên của nó bằng cách phản chiếu và thêm các kích thước (boolean & byte = 1 byte, short & char = 2 byte, v.v.), làm việc theo cách phân cấp thành viên. Nhưng đó là tẻ nhạt và tốn kém và cuối cùng làm điều tương tự mà chiến lược tuần tự hóa sẽ làm.


3
Tôi sẽ tuần tự hóa nó thành một byte [] bằng cách sử dụng ByteArrayOutputStream. Nó sẽ nhanh hơn rất nhiều so với việc viết nó ra một tập tin.
ScArcher2

@KorayTugay Xác định kích thước byte của một đối tượng đã là một hoạt động tốn kém. Ghi từng đối tượng vào đĩa để xác định kích thước, sẽ khiến nó thu thập dữ liệu ...
HammerNL

1
Định dạng đối tượng được tuần tự hóa hoàn toàn khác với định dạng của đối tượng trong bộ nhớ heap. Đáng chú ý nhất, một bộ mô tả cho lớp của đối tượng (và tất cả các siêu lớp tuần tự hóa của nó) được ghi vào luồng. Vì vậy, viết một thể hiện đơn giản của việc java.lang.Integertạo ra khoảng 80 byte, trong đó biểu diễn heap thường là 32 (không giống như biểu diễn luồng đối tượng, biểu diễn heap phụ thuộc vào kích thước con trỏ và căn chỉnh đối tượng). Ngược lại, một nulltham chiếu nối tiếp yêu cầu một byte thay vì bốn hoặc tám byte trong bộ nhớ heap.
Holger
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.