ใน Java วิธีที่ดีที่สุดในการกำหนดขนาดของวัตถุคืออะไร?


617

ฉันมีแอปพลิเคชันที่อ่านไฟล์ CSV พร้อมแถวข้อมูลจำนวนมาก ฉันให้ผู้ใช้บทสรุปของจำนวนแถวขึ้นอยู่กับประเภทของข้อมูล แต่ฉันต้องการที่จะให้แน่ใจว่าผมไม่ได้อ่านในแถวมากเกินไปของข้อมูลและสาเหตุOutOfMemoryErrors แต่ละแถวแปลเป็นวัตถุ มีวิธีง่ายๆในการหาขนาดของวัตถุนั้นโดยทางโปรแกรมหรือไม่? มีการอ้างอิงที่กำหนดวิธีขนาดใหญ่ประเภทและการอ้างอิงวัตถุดั้งเดิมใช้สำหรับVM?

ตอนนี้ฉันมีโค้ดที่อ่านได้มากถึง32,000 แถวแต่ฉันก็อยากมีโค้ดที่อ่านมากที่สุดเท่าที่จะเป็นไปได้จนกว่าฉันจะใช้หน่วยความจำ32MB อาจเป็นคำถามที่แตกต่างกัน แต่ฉันก็ยังอยากจะรู้


ฉันเพิ่มตัวแทนของฉันด้วยการกำหนดค่า mvn และอธิบายวิธีการที่นี่: stackoverflow.com/a/36102269/711855
juanmf

คำตอบ:


461

คุณสามารถใช้แพ็คเกจ java.lang.instrument

รวบรวมและวางคลาสนี้ใน 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);
    }
}

เพิ่มสิ่งต่อไปนี้ในของคุณMANIFEST.MF:

Premain-Class: ObjectSizeFetcher

ใช้ getObjectSize:

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

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

เรียกใช้ด้วย:

java -javaagent:ObjectSizeFetcherAgent.jar C

2
@ สเตฟานคำใบ้ที่ดี! คุณช่วยกรุณาบอกสิ่งที่จะเป็นขนาดของbyte[0], byte[1], byte[5], int[0], int[1], int[2]ใช้วิธีการที่คุณอธิบาย? มันจะดีถ้าผลรวมค่าใช้จ่ายสำหรับความยาวของอาเรย์และการจัดตำแหน่งหน่วยความจำ
dma_k

8
ฉันลองสิ่งนี้และได้ผลลัพธ์ที่แปลกและไม่ช่วยเหลือ เงื่อนไขคือ 32 เสมอโดยไม่คำนึงถึงขนาด ฉันคิดว่านี่อาจเป็นขนาดตัวชี้ แต่สำหรับคลาสที่ไม่เปลี่ยนรูปแบบอื่นที่ฉันสร้างขึ้นฉันได้ 24 มันใช้งานได้ดีสำหรับการใช้แบบดั้งเดิม แต่จากนั้นคุณไม่จำเป็นต้องใช้โปรแกรมเพื่อบอกคุณว่าตัวอักษรตัวใหญ่แค่ไหน
Brel

6
@Brel โซลูชันนี้เป็นเพียง "การประมาณจำนวนของที่เก็บข้อมูลที่ใช้โดยวัตถุที่ระบุ" ตามที่ระบุไว้ในเอกสารประกอบ นอกจากนี้ฉันสมมติว่าผู้เขียนตัดสินใจตั้งค่าขนาดของสตริงเป็น 32 ไบต์ (ตัวชี้เท่านั้น) เนื่องจากพูลสตริงของ Java ซึ่งทำให้ยากที่จะพูดไม่ว่าจะใช้อินสแตนซ์สตริงร่วมกัน (เก็บไว้ในพูล) หรือ ท้องถิ่นและไม่ซ้ำกันในชั้นเรียน
อังเดรฉัน

11
ฉันจะใช้ ObjectSizeFetcher ได้อย่างไรถ้าไม่ส่งออก jar? ฉันมีโครงการทดสอบจาวาในคราส
Yura Shinkarev

3
@brel เหตุผลที่ String มีขนาดเพียง 32 ไบต์โดยไม่คำนึงถึงความยาวจริงเพราะส่วนความยาวผันแปรของสตริงจะถูกเก็บไว้ในอักขระ [] ซึ่งเป็นวัตถุของตัวเอง ในการรับขนาดที่แท้จริงของวัตถุคุณต้องเพิ่มขนาดของตัวเองและขนาดของแต่ละวัตถุที่อ้างอิง
tombrown52

117

คุณควรใช้jolซึ่งเป็นเครื่องมือที่พัฒนาขึ้นเป็นส่วนหนึ่งของโครงการ OpenJDK

JOL (Java Object Layout) เป็นกล่องเครื่องมือขนาดเล็กเพื่อวิเคราะห์โครงร่างของวัตถุใน JVM เครื่องมือเหล่านี้ใช้ Unsafe, JVMTI และ Serviceability Agent (SA) เป็นอย่างมากเพื่อถอดรหัสรูปแบบของวัตถุรอยเท้าและการอ้างอิงที่แท้จริง สิ่งนี้ทำให้ JOL มีความแม่นยำมากกว่าเครื่องมืออื่น ๆ ที่ใช้กับกองทิ้งข้อมูลจำเพาะของสมมติฐาน ฯลฯ

เพื่อให้ได้ขนาดของวิทยาการอ้างอิงและองค์ประกอบมากมาย, VMSupport.vmDetails()การใช้งาน บน Oracle JDK 1.8.0_40 ทำงานบน Windows 64 บิต (ใช้สำหรับตัวอย่างต่อไปนี้ทั้งหมด) วิธีนี้จะส่งคืน

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]

คุณสามารถรับขนาดตื้นของอินสแตนซ์ของวัตถุโดยใช้ClassLayout.parseClass(Foo.class).toPrintable()(เลือกที่จะส่งตัวอย่างไปtoPrintable) นี่เป็นเพียงพื้นที่ที่ใช้โดยอินสแตนซ์เดียวของคลาสนั้น มันไม่รวมวัตถุอื่น ๆ ที่อ้างอิงโดยชั้นเรียนนั้น มันไม่รวมถึงค่าใช้จ่าย VM สำหรับส่วนหัวของวัตถุที่จัดสนามและ padding สำหรับ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

GraphLayout.parseInstance(obj).toFootprint()คุณจะได้รับมุมมองสรุปของขนาดความลึกของวัตถุเช่นในการใช้ แน่นอนว่าวัตถุบางอย่างในรอยพระพุทธบาทนั้นอาจถูกใช้ร่วมกัน (อ้างอิงจากวัตถุอื่นด้วย) ดังนั้นจึงเป็นการ overapproximation ของพื้นที่ที่สามารถเรียกคืนได้เมื่อวัตถุนั้นถูกเก็บขยะ สำหรับผลลัพธ์ของPattern.compile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$")(นำมาจากคำตอบนี้ ) jol รายงาน footprint ทั้งหมดที่มี 1,840 ไบต์ซึ่งมีเพียง 72 ตัวอย่างเท่านั้นที่เป็นอินสแตนซ์ Pattern

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)

หากคุณใช้แทนGraphLayout.parseInstance(obj).toPrintable()jol จะแจ้งให้คุณทราบถึงการกำหนดที่อยู่ขนาดประเภทค่าและเส้นทางของฟิลด์ไปยังวัตถุที่อ้างอิงแต่ละรายการ แต่โดยปกติแล้วรายละเอียดนั้นจะมีประโยชน์มากเกินไป สำหรับตัวอย่างรูปแบบต่อเนื่องคุณอาจได้รับสิ่งต่อไปนี้ (ที่อยู่อาจมีการเปลี่ยนแปลงระหว่างการทำงาน)

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)

รายการ "(อย่างอื่น)" อธิบายวัตถุอื่น ๆ ในฮีปที่ไม่ได้เป็นส่วนหนึ่งของกราฟวัตถุนี้

เอกสาร jol ที่ดีที่สุดคือตัวอย่าง jolในที่เก็บ jol ตัวอย่างแสดงการดำเนินการ jol ทั่วไปและแสดงวิธีที่คุณสามารถใช้ jol เพื่อวิเคราะห์ VM และภายในตัวรวบรวมขยะ


18
คำตอบนี้ควรมี upvotes มากกว่านี้ ตัวเลือกที่ดีมากในการตรวจสอบแน่นอน แก้ไข: ตรวจสอบว่ามีการเพิ่มปีนี้ในขณะที่คำถามถูกถามใน '08 อาจเป็นตัวเลือกที่ดีที่สุดและง่ายที่สุดในการทำสิ่งที่ OP ถามในขณะนี้
ค่าเช่า

4
ผู้เขียนเครื่องมือเขียนบล็อกโพสต์เกี่ยวกับ Jol
Mike

2
เพื่อกำหนดขนาดของ Object "obj" ให้ใช้: org.openjdk.jol.info.GraphLayout.parseInstance (obj) .totalSize ();
ความแข็งแรง

โปรดทราบว่าอยู่ในขณะนี้vmDetails VM.current().details()
Miha_x64

ตรวจสอบGraphLayout.parseInstance(instance).toFootprint()ฉันพบว่ามันมีประโยชน์มากกว่าที่จะเข้าใจขนาดของวัตถุ
Mugen

82

ฉันพบคลาส java "jdk.nashorn.internal.ir.debug.ObjectSizeCalculator" โดยบังเอิญใน jdk ซึ่งใช้งานง่ายและดูเหมือนว่ามีประโยชน์ในการกำหนดขนาดของวัตถุ

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]));

ผล:

164192
48
16
48
416

3
เช่นเดียวกันที่นี่ฉันลองใช้วิธีแก้ไขปัญหาอื่นที่เสนอข้างต้นแล้วเจอ ObjectSizeCalculator ผมเชื่อว่าไม่มีใครกล่าวถึงถ้าก่อนเพราะมันถูกนำมาใช้เมื่อเร็ว ๆ นี้ JDK 8 เป็นส่วนหนึ่งของโครงการNashorn อย่างไรก็ตามฉันไม่พบเอกสารอย่างเป็นทางการเกี่ยวกับคลาสนี้บนเว็บ
Henrique Gontijo

ดูเหมือนจะไม่พิจารณาความยาวของสตริง มันเกี่ยวกับขนาดของกองซ้อนหรือเปล่า?
jontejj

1
ฉันมี hashmap ที่ com.carrotsearch.RamUsageEstimator ส่งคืนประมาณครึ่งหนึ่งของ ObjectSizeCalculator อันไหนจริงหรือ? - อันไหนที่เชื่อถือได้มากกว่า?
badera

9
โปรดทราบว่าObjectSizeCalculatorได้รับการสนับสนุนเฉพาะใน HotSpot VM
kellanburket

74

หลายปีก่อน Javaworld มีบทความเกี่ยวกับการกำหนดขนาดของวัตถุคอมโพสิตและอาจซ้อนกันของ Javaพวกเขาโดยทั่วไปผ่านการสร้างการใช้งาน sizeof () ใน Java โดยทั่วไปแล้ววิธีการต่อไปนี้จะสร้างงานอื่นที่ผู้คนระบุขนาดของวัตถุดั้งเดิมและวัตถุ Java ทั่วไปแล้วนำความรู้นั้นไปใช้กับวิธีการที่เรียกกราฟกราฟวัตถุซ้ำ ๆ

มันจะมีความแม่นยำน้อยกว่าการติดตั้ง C แบบดั้งเดิมเสมอเนื่องจากสิ่งต่าง ๆ เกิดขึ้นหลังฉากของคลาส แต่ควรเป็นตัวบ่งชี้ที่ดี

อีกทางหนึ่งคือโครงการ SourceForge ที่เรียกว่าsizeofที่ให้บริการไลบรารี Java5 พร้อมกับการปรับใช้ sizeof ()

PS อย่าใช้วิธีการทำให้เป็นอนุกรมไม่มีความสัมพันธ์ระหว่างขนาดของวัตถุที่ทำให้เป็นอนุกรมและปริมาณของหน่วยความจำที่ใช้เมื่อใช้งานจริง


6
ยูทิลิตี้ sizeof น่าจะเป็นวิธีที่เร็วที่สุด มันเป็นสิ่งที่ Stefan พูด แต่บรรจุในขวดพร้อมใช้งานแล้ว
Alexandre L บอก

62

ประการแรก "ขนาดของวัตถุ" ไม่ใช่แนวคิดที่กำหนดไว้อย่างดีใน Java คุณอาจหมายถึงวัตถุนั้นมีเพียงสมาชิกวัตถุและวัตถุทั้งหมดที่อ้างถึง (กราฟอ้างอิง) คุณอาจหมายถึงขนาดในหน่วยความจำหรือขนาดของดิสก์ และ JVM ได้รับอนุญาตให้ปรับแต่งสิ่งต่าง ๆ เช่น Strings

วิธีเดียวที่ถูกต้องคือถาม JVM ด้วย profiler ที่ดี (ฉันใช้YourKit ) ซึ่งอาจไม่ใช่สิ่งที่คุณต้องการ

อย่างไรก็ตามจากคำอธิบายข้างต้นดูเหมือนว่าแต่ละแถวจะมีข้อมูลในตัวเองและไม่มีต้นไม้อ้างอิงขนาดใหญ่ดังนั้นวิธีการเรียงลำดับอาจจะเป็นการประมาณที่ดีสำหรับ JVM ส่วนใหญ่ วิธีที่ง่ายที่สุดในการทำเช่นนี้คือ:

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

โปรดจำไว้ว่าหากคุณมีวัตถุที่มีการอ้างอิงทั่วไปนี้ จะไม่ให้ผลลัพธ์ที่ถูกต้องและขนาดของการทำให้เป็นอนุกรมจะไม่ตรงกับขนาดในหน่วยความจำเสมอไป แต่เป็นการประมาณที่ดี รหัสจะมีประสิทธิภาพมากกว่านี้เล็กน้อยหากคุณกำหนดขนาด ByteArrayOutputStream เป็นค่าที่เหมาะสม


2
ฉันชอบวิธีนี้ ขนาดของวัตถุที่คุณไปนั้นไกลแค่ไหน
สีน้ำตาล,

1
ง่ายมากและมีประสิทธิภาพ วิธีอื่น ๆ ยุ่งเกินไป (โดยเฉพาะภายใน Eclipse RCP) ขอบคุณ
marcolopes

19
การทำให้เป็นอนุกรมจะไม่ติดตามตัวแปรชั่วคราวและวิธีการเรียงลำดับเริ่มต้นจะเขียนสตริงใน UTF-8 ดังนั้นอักขระ ANSI ใด ๆ จะใช้หนึ่งไบต์เท่านั้น หากคุณมีหลายสายขนาดของคุณจะไกลเท่าที่จะไร้ประโยชน์
TMN

1
ขณะนี้อาจไม่ให้ขนาดที่แน่นอนสำหรับความต้องการของฉันฉันต้องการเพียงการเปรียบเทียบระหว่าง 2 วัตถุและ SizeOf จะไม่เริ่มต้นจากเว็บแอป ขอบคุณ!
ไอแซค

1
คำแนะนำที่ดีของYourKit ทางเลือกอื่นคือVirtualVMและjvmmonitor
angelcervera

38

หากคุณต้องการทราบจำนวนหน่วยความจำที่ใช้ใน JVM ของคุณและจำนวนหน่วยความจำที่ว่างคุณสามารถลองดังนี้:

// 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();

แก้ไข: ฉันคิดว่านี่อาจเป็นประโยชน์ในขณะที่ผู้เขียนคำถามระบุด้วยว่าเขาต้องการมีตรรกะที่จัดการ "อ่านได้หลายแถวเท่าที่จะทำได้จนกว่าฉันจะใช้หน่วยความจำ 32MB"


24
นี่ไม่ใช่วิธีแก้ปัญหาที่ดีอย่างที่คุณไม่เคยรู้เมื่อมีการรวบรวมขยะเกิดขึ้นหรือจำนวนหน่วยความจำเพิ่มเติมจะถูกจัดสรรให้กับกองพร้อมกัน
Nick Fortescue

5
นั่นเป็นเรื่องจริงและฉันก็ไม่ได้ตั้งใจจะตอบคำถามหลักของโพสต์นี้ แต่มันอาจช่วยให้เขารู้โดยทางโปรแกรมเมื่อเขาเข้าใกล้ขนาดที่ใหญ่ที่สุด
แมตต์ b

1
ปัญหาอื่นของการแก้ปัญหานี้คือเมื่อคุณอยู่ในสภาพแวดล้อมแบบมัลติเธรด (เช่นในเว็บเซิร์ฟเวอร์) อาจเป็นไปได้ว่าเธรดอื่นอยู่ในการทำงานและการใช้หน่วยความจำ ด้วยการประมาณนี้คุณกำลังคำนวณหน่วยความจำที่ใช้ในเครื่องเสมือนทั้งหมด
angelcervera

1
ข้อเสียอีกอย่างหนึ่งคือ freeMemory ส่งคืนการประมาณ ลองสร้างวัตถุ javax.crypto.Cipher ความแตกต่างระหว่างการโทรสองครั้งกับ freeMemory (เพื่อประมาณขนาดของการเข้ารหัส) นั้นไม่คงที่!
Eugen

1
ฉันเชื่อว่าคุณสามารถบังคับให้มีการรวบรวมขยะเพื่อให้คุณสามารถทำบางสิ่งในแนวทางนี้
matanster

24

ย้อนกลับไปเมื่อฉันทำงานที่ Twitter ฉันเขียนโปรแกรมสำหรับคำนวณขนาดวัตถุที่ลึก โดยคำนึงถึงรุ่นหน่วยความจำที่แตกต่างกัน (32 บิต, โอ๊ะบีบอัด, 64- บิต), แพ็ดดิ้ง, แพ็ดคลาสย่อยทำงานอย่างถูกต้องในโครงสร้างข้อมูลแบบวงกลมและอาร์เรย์ คุณสามารถรวบรวมไฟล์. java หนึ่งไฟล์นี้ได้ มันไม่มีการอ้างอิงภายนอก:

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


1
Szia! ฉันก็อยากจะตะโกนนำเสนอของคุณเช่นกัน: สไลด์ 15-20 นั้นยอดเยี่ยมที่จะช่วยให้คุณรู้สึกถึงสัญชาตญาณในการตัดสินใจโครงสร้างข้อมูลที่หลากหลาย ขอบคุณสำหรับการโพสต์นั้น!
ลุค Usherwood

16
"มันไม่มีการพึ่งพาภายนอก" - ตั้งแต่เมื่อฝรั่งไม่พึ่งพาภายนอก?
l4mpi

2
ลักษณะคล้ายกับgithub.com/JetBrains/jdk8u_nashorn/blob/master/src/jdk/nashorn/... ? : O
Francesco

Guave เป็นการพึ่งพาภายนอก
Mert Serimer

18

คำตอบอื่น ๆ ส่วนใหญ่มีขนาดที่ตื้นเช่นขนาดของ HashMap โดยไม่มีปุ่มหรือค่าใด ๆ ซึ่งไม่น่าจะเป็นสิ่งที่คุณต้องการ

โปรเจ็กต์ jamm ใช้แพ็กเกจ java.lang.instrumentation ด้านบน แต่ใช้งานทรีและสามารถใช้หน่วยความจำอย่างลึกซึ้งได้

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm

ในการใช้ MemoryMeter ให้เริ่มต้น JVM ด้วย "-javaagent: /jamm.jar"


11

คุณต้องเดินวัตถุโดยใช้การสะท้อน ระวังอย่างที่คุณทำ:

  • เพียงการจัดสรรวัตถุมีค่าใช้จ่ายใน JVM จำนวนแตกต่างกันไปตาม JVM ดังนั้นคุณอาจทำให้ค่านี้เป็นพารามิเตอร์ อย่างน้อยก็ทำให้มันคงที่ (8 ไบต์?) และนำไปใช้กับทุกสิ่งที่ได้รับการจัดสรร
  • เพียงเพราะในbyteทางทฤษฎีแล้ว 1 ไบต์ไม่ได้หมายความว่ามันใช้หน่วยความจำเพียงอันเดียว
  • จะมีลูปในการอ้างอิงวัตถุดังนั้นคุณจะต้องเก็บ a HashMapหรือ somesuch โดยใช้ object- เท่ากับเป็นเครื่องมือเปรียบเทียบเพื่อกำจัดลูปที่ไม่มีที่สิ้นสุด

@ jodonnell: ฉันชอบความเรียบง่ายของการแก้ปัญหาของคุณ แต่วัตถุจำนวนมากไม่ได้เป็นแบบอนุกรม (ดังนั้นสิ่งนี้จะทำให้เกิดข้อยกเว้น), เขตข้อมูลสามารถเป็นชั่วคราวและวัตถุสามารถแทนที่วิธีมาตรฐาน


ขนาดดั้งเดิมต่าง ๆ ที่กำหนดไว้ในข้อกำหนดของ Java ไม่ได้หรือไม่ (§2.4.1)
erickson

4
ไม่ได้อยู่ในความหมายของ "หน่วยความจำมีจำนวนเท่าใด" ซึ่งเป็นคำถาม เฉพาะในแง่ของวิธีการใช้งาน ยกตัวอย่างเช่นไบต์, ตัวอักษรและกางเกงขาสั้นใช้เวลาถึงคำทั้งในกอง Java ถึงแม้ว่าพวกเขาทำงานกับการปัดเศษ ฯลฯ ..
เจสันโคเฮน

1
เสียงนี้คล้ายกับการวัดขนาดที่แสดงโดยไฮนซ์ในจดหมายข่าวของเขา # 78: javaspecialists.eu/archive/Issue078.html ฉันใช้มัน วิธีการของเขาใช้งานได้
Peter Kofler

8

คุณต้องวัดด้วยเครื่องมือหรือประเมินด้วยมือและขึ้นอยู่กับ JVM ที่คุณใช้

มีบางค่าใช้จ่ายคงที่ต่อวัตถุ เป็นเฉพาะ JVM แต่ฉันมักจะประมาณ 40 ไบต์ จากนั้นคุณต้องดูสมาชิกของชั้นเรียน การอ้างอิงวัตถุคือ 4 (8) ไบต์ใน JVM 32- บิต (64- บิต) ประเภทดั้งเดิมคือ:

  • บูลีนและไบต์: 1 ไบต์
  • ถ่านและสั้น: 2 ไบต์
  • int และลอย: 4 ไบต์
  • ยาวและสอง: 8 ไบต์

อาร์เรย์ปฏิบัติตามกฎเดียวกัน นั่นคือมันคือการอ้างอิงวัตถุเพื่อให้มีขนาด 4 (หรือ 8) ไบต์ในวัตถุของคุณและจากนั้นความยาวของมันจะถูกคูณด้วยขนาดขององค์ประกอบ

การพยายามทำด้วยโปรแกรมโดยใช้การเรียกRuntime.freeMemory()เพียงแค่ไม่ให้ความแม่นยำมากนักเนื่องจากการโทรแบบอะซิงโครนัสกับตัวรวบรวมขยะ ฯลฯ การทำโปรไฟล์ฮีปด้วย -Xrunhprof หรือเครื่องมืออื่น ๆ จะให้ผลลัพธ์ที่แม่นยำที่สุด


@erickson ฉันไม่แน่ใจเกี่ยวกับ sizeof (boolean) == 1 ดูที่กระทู้นี้ ( stackoverflow.com/questions/1907318/… ) คุณช่วยแสดงความคิดเห็นเกี่ยวกับเรื่องนี้ได้ไหม?
dma_k

2
@dma_k, Java ไม่มีบูลีนจริง ๆ ขนาดของบูลเป็น 4bytes boolean[]อาร์เรย์ภายนอกและภายใน จริง ๆ แล้ว primitives ที่ไม่ใช่ double / long type คือ 4 ไบต์ หลังมี 8 (คำตอบที่ผิดทำให้พวกเขาเป็น 4 เกินไป)
bestsss

@bestsss: เพื่อให้แม่นยำยิ่งขึ้นการจัดสรรหน่วยความจำน้อยที่สุดขึ้นอยู่กับแพลตฟอร์มและการใช้งาน JVM นอกจากนี้ยังมีการจัดวัตถุบนกองให้สอดคล้องด้วยดังนั้นหลังจากรวมทุกขนาดแล้วจึงต้องปัดเศษขึ้น
dma_k

6

java.lang.instrument.Instrumentationระดับให้เป็นวิธีที่ดีที่จะได้รับขนาดของวัตถุ Java แต่คุณจะต้องกำหนดpremainและเรียกใช้โปรแกรมของคุณกับตัวแทนจาวา นี่เป็นเรื่องที่น่าเบื่อมากเมื่อคุณไม่ต้องการตัวแทนใด ๆ จากนั้นคุณจะต้องให้ตัวแทน Jar จำลองกับใบสมัครของคุณ

ดังนั้นผมจึงมีวิธีการแก้ปัญหาทางเลือกโดยใช้ระดับจากUnsafe sun.miscดังนั้นเมื่อพิจารณาถึงการจัดวางวัตถุฮีปตามสถาปัตยกรรมโปรเซสเซอร์และการคำนวณออฟเซ็ตสูงสุดของฟิลด์คุณสามารถวัดขนาดของวัตถุ Java ได้ ในตัวอย่างด้านล่างฉันใช้คลาสเสริมUtilUnsafeเพื่อรับการอ้างอิงถึงsun.misc.Unsafeวัตถุ

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() { }
}

วิธีการที่น่าสนใจ แต่สิ่งนี้ไม่ได้สมมติว่าวัตถุและที่เก็บข้อมูลของเขตข้อมูลไม่ได้แยกส่วน
nicoulaj

ใช่และฉันไม่รู้จักการใช้ JVM ใด ๆ ที่ทำให้การแตกแฟรกเมนต์ดังกล่าว
Miguel Gamboa

ฉันไม่เข้าใจ การแตกแฟรกเมนต์ไม่ใช่ตัวเลือก :) ลองมาตัวอย่างของวัตถุ C ซึ่งเก็บไว้เป็นเขตข้อมูลของวัตถุ A และ B มันไม่เปลี่ยนทั้งสิ่งทั้ง A หรือ B หรือไม่?
nicoulaj

ขออภัยฉันไม่เข้าใจมุมมองของคุณ ตามการตีความของฉันในวัตถุ Java ไม่สามารถเก็บไว้ในวัตถุอื่น ๆ เช่นเกิดขึ้นกับโครงสร้าง C หรือประเภทค่าใน. Net ดังนั้นเมื่อคุณพูดว่า:“ วัตถุ C ซึ่งถูกจัดเก็บเป็นเขตข้อมูลของวัตถุ A และ B” ซึ่งหมายความว่าวัตถุ A และ B มีเขตข้อมูลที่เก็บการอ้างอิง (ตัวชี้) ไปยังวัตถุ C จากนั้นขนาดของ A และ B เท่ากับ ออฟเซ็ตของฟิลด์นั้นบวกขนาดของการอ้างอิง (ตัวชี้) ไปยังวัตถุ C และขนาดของการอ้างอิงคือขนาดของคำเดียว
Miguel Gamboa

โอ้ใช่เรากำลังพูดถึงขนาดที่ตื้น ความผิดฉันเอง.
nicoulaj

6

นอกจากนี้ยังมีหน่วยความจำ Measurerเครื่องมือ (ก่อนที่Google Codeตอนนี้บนGitHub ) ซึ่งเป็นเรื่องง่ายและตีพิมพ์ในเชิงพาณิชย์ที่เหมาะสำหรับApache 2.0 ใบอนุญาตตามที่กล่าวไว้ในคำถามที่คล้ายกัน

มันก็ต้องมีอาร์กิวเมนต์บรรทัดคำสั่งเพื่อล่าม java หากคุณต้องการวัดปริมาณการใช้หน่วยความจำไบต์ แต่ดูเหมือนว่าจะทำงานได้ดีอย่างน้อยในสถานการณ์ที่ฉันได้ใช้มัน


4

โดยไม่ต้องยุ่งกับเครื่องมือและอื่น ๆ และถ้าคุณไม่จำเป็นต้องรู้ขนาดของวัตถุที่แน่นอนคุณสามารถไปด้วยวิธีการดังต่อไปนี้:

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

do your job here

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

วิธีนี้คุณจะอ่านหน่วยความจำที่ใช้ก่อนและหลังและเรียก GC ก่อนที่จะใช้หน่วยความจำที่ใช้แล้วคุณจะลด "สัญญาณรบกวน" เกือบเป็น 0

เพื่อผลลัพธ์ที่น่าเชื่อถือมากขึ้นคุณสามารถรันงานของคุณได้ n ครั้งแล้วแบ่งหน่วยความจำที่ใช้ไปด้วย n เพื่อรับจำนวนหน่วยความจำที่ใช้ ยิ่งไปกว่านั้นคุณสามารถวิ่งได้ทุกอย่างมากขึ้นและทำการเฉลี่ย


5
ไม่System.gc()เพียงแค่แจ้งว่าคุณต้องการ GC ใช่ไหม ไม่รับประกันว่า GC จะถูกเรียกใช้ทั้งหมด
Raildex

@ดีจริงๆ. สิ่งนี้ไม่ปลอดภัยเพราะคุณอาจไม่เคยทำสิ่งที่ GC ทำหรือมีผลต่อหน่วยความจำระหว่างบรรทัดของคุณ ดังนั้น "ระหว่าง" สองวิธีหน่วยความจำอิสระ GC สามารถเพิ่มพื้นที่ว่างมากขึ้นซึ่งคุณไม่ได้พิจารณาดังนั้นวัตถุของคุณจะดูเล็กลง
Mert Serimer

@MertSerimer "ไม่ปลอดภัย" อยู่ในระดับที่แตกต่างกันโดยสิ้นเชิงสำหรับฉัน: ที่จริงแล้วมันก็ไม่ค่อยแม่นยำเท่าที่ควร นอกจากนี้คุณไม่สามารถขับ GC ได้ (ตามที่ Raildex ระบุไว้) แต่สำหรับกรณีนี้ฉันก็แนะนำให้ใส่ในรอบ นี่เป็นเพียงระบบที่รวดเร็วและสกปรกและโดยประมาณซึ่งใช้งานได้หากผลลัพธ์ไม่จำเป็นต้องเชื่อถือได้มากตามที่ระบุไว้
จริงๆ

มีปัญหามากมายเกี่ยวกับสิ่งนี้ แต่มันจะทำให้คุณดีมาก
markthegrea

3

นี่คือยูทิลิตี้ที่ฉันสร้างขึ้นโดยใช้ตัวอย่างที่เชื่อมโยงเพื่อจัดการ OOP แบบ 32 บิต, 64- บิตและ 64 บิตด้วย OOP ที่ถูกบีบอัด มันใช้sun.misc.Unsafeมันใช้

มันใช้Unsafe.addressSize()ในการรับขนาดของตัวชี้พื้นเมืองและUnsafe.arrayIndexScale( Object[].class )ขนาดของการอ้างอิง Java

มันใช้ชดเชยสนามของชั้นที่รู้จักกันในการทำงานขนาดฐานของวัตถุ

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 );
    }
}

คุณทดสอบคลาสนี้ด้วยค่าหรือไม่ ฉันพยายาม แต่สำหรับฉันแล้วค่าที่ไม่ถูกต้อง !!!
Débora

1
ค่าที่ให้ฉันสำหรับวัตถุอย่างง่ายมีค่าประมาณ แต่ถูกลบออกด้วยปัจจัย 10 สำหรับรายการที่มีวัตถุ 1mio ยังทำงานได้ดีมาก!
Michael Böckling

น่าสนใจ ฉันได้ทำการทดสอบโดยใช้ JDK7u67 บน Windows 7 x64 และ Linux 2.6.16 / x86_64 โดยใช้โหมดที่อยู่ 32 บิต / 64 บิต / oop แต่ละโหมด ฉันได้เปรียบเทียบกับการถ่ายโอนข้อมูลหน่วยความจำที่วิเคราะห์ใน Eclipse Memory Analyzer 1.3.x คุณกำลังใช้การตั้งค่าแบบใด คุณมีตัวอย่างที่ฉันลองได้ไหม
dlaudams

ทางเลือกที่ดีที่สุดที่ฉันสามารถทำได้ ฉันไม่สามารถใช้Instrumentationเพราะฉันไม่ได้เริ่มต้น Tomcat ObjectSizeCalculatorเพราะไม่แน่ใจในประเภท VM (HotSpot) และJOLถั่วสปริง bacouse ฉันใช้สิ่งนี้และเพิ่มพารามิเตอร์ที่สองสำหรับการละเว้น singletons ได้แก่AbstractRefreshableApplicationContext.getBeanFactory().getSingletonMutex()และinternalSizeOfรหัสrefactor เพื่อละเว้น Class และ Enum
Perlos

สำหรับการเปรียบเทียบผลลัพธ์ให้ใช้ ObjectSizeCalculator (คำนวณทั้งเซิร์ฟเวอร์ 1GB ถึง 10 วินาที) JOL ทำให้เกิด MemError (ไม่ได้ถูกเปิดใช้งาน 6GB) และฉันไม่ได้ผลลัพธ์เดียวกันอาจเป็นเพราะ enums
Perlos

3

ฉันกำลังมองหาการคำนวณรันไทม์ของขนาดวัตถุที่ตรงตามข้อกำหนดต่อไปนี้:

  • สามารถใช้งานได้ที่ runtime โดยไม่จำเป็นต้องมีเครื่องมือ
  • ทำงานร่วมกับ Java 9+ โดยไม่ต้องเข้าถึง Unsafe
  • ขึ้นอยู่กับคลาสเท่านั้น ไม่ใช่ sizeOf ที่ลึกที่คำนึงถึงความยาวสตริงความยาวอาร์เรย์ ฯลฯ

ต่อไปนี้เป็นไปตามรหัสหลักของบทความผู้เชี่ยวชาญต้นฉบับของจาวา ( https://www.javaspecialists.eu/archive/Issue078.html ) และไม่กี่บิตจากรุ่นที่ไม่ปลอดภัยในคำตอบสำหรับคำถามนี้

ฉันหวังว่าบางคนจะพบว่ามีประโยชน์

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

ไม่มีวิธีการโทรถ้านั่นคือสิ่งที่คุณขอ ด้วยการวิจัยเล็กน้อยฉันคิดว่าคุณสามารถเขียนของคุณเอง อินสแตนซ์เฉพาะมีขนาดคงที่ซึ่งได้มาจากจำนวนการอ้างอิงและค่าดั้งเดิมพร้อมข้อมูลการทำบัญชีอินสแตนซ์ คุณเพียงแค่เดินกราฟวัตถุ ยิ่งประเภทแถวน้อยลงเท่าใดก็ยิ่งง่ายเท่านั้น

หากนั่นช้าเกินไปหรือมีปัญหามากกว่าที่ควรจะเป็นก็คือการนับกฎของหัวแม่มือ


2

ฉันเขียนการทดสอบอย่างรวดเร็วหนึ่งครั้งเพื่อประมาณค่าทันที:

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();
    }

}

แนวคิดทั่วไปคือการจัดสรรวัตถุและวัดการเปลี่ยนแปลงในพื้นที่ว่างของกอง ที่ถูกที่สำคัญgetFreeMemory()ซึ่งขอวิ่ง GC และรอให้มีการรายงานขนาดกองฟรีเพื่อรักษาเสถียรภาพ ผลลัพธ์ของข้างต้นคือ:

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

สิ่งใดที่เราคาดหวังกำหนดพฤติกรรมการจัดตำแหน่งและค่าส่วนหัวของบล็อกฮีปที่เป็นไปได้

วิธีการใช้เครื่องมือรายละเอียดในคำตอบที่ยอมรับได้ที่นี่ถูกต้องที่สุด วิธีการที่ฉันอธิบายนั้นถูกต้อง แต่ภายใต้เงื่อนไขที่ควบคุมซึ่งไม่มีเธรดอื่น ๆ กำลังสร้าง / ทิ้งวัตถุ


2

เพียงใช้จาวา Visual VM

มีทุกสิ่งที่คุณต้องการในการสร้างโปรไฟล์และแก้ไขปัญหาหน่วยความจำ

นอกจากนี้ยังมีคอนโซล OQL (Object Query Language) ซึ่งช่วยให้คุณทำสิ่งที่มีประโยชน์มากมายซึ่งหนึ่งในนั้นคือ sizeof(o)


2

เมื่อใช้ JetBrains IntelliJ ขั้นแรกให้เปิดใช้งาน "แนบตัวแทนหน่วยความจำ" ในไฟล์ | การตั้งค่า | Build, Execution, Deployment | ดีบักเกอร์

เมื่อทำการดีบั๊กให้คลิกขวาที่ตัวแปรที่สนใจแล้วเลือก "คำนวณขนาดที่คงอยู่": คำนวณขนาดที่สะสม


1

คำตอบของฉันขึ้นอยู่กับรหัสที่มาจาก Nick รหัสนั้นวัดจำนวนไบต์ทั้งหมดที่ครอบครองโดยวัตถุที่ต่อเนื่องกัน ดังนั้นนี่คือการวัดสิ่งที่ทำให้เป็นอนุกรม + รอยเท้าหน่วยความจำวัตถุธรรมดา (เพียงยกตัวอย่างเช่นintและคุณจะเห็นว่าจำนวนไบต์ทั้งหมดต่อเนื่องไม่ได้4) ดังนั้นหากคุณต้องการรับจำนวนไบต์ดิบที่ใช้กับวัตถุของคุณอย่างถูกต้องคุณต้องแก้ไขรหัสนั้นเล็กน้อย ชอบมาก

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;
    }    

}

ฉันได้ทดสอบวิธีนี้ด้วยประเภทดั้งเดิม, String และในชั้นเรียนเล็ก ๆ น้อย ๆ อาจมีกรณีไม่ครอบคลุม


UPDATE:ตัวอย่างที่แก้ไขเพื่อรองรับการคำนวณ footprint หน่วยความจำของวัตถุอาร์เรย์


0

คุณสามารถสร้างกองดัมพ์ (ตัวอย่างเช่น jmap) แล้ววิเคราะห์เอาต์พุตเพื่อค้นหาขนาดวัตถุ นี่เป็นโซลูชันออฟไลน์ แต่คุณสามารถตรวจสอบขนาดที่ตื้นและลึก ฯลฯ


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

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

ขนาดช่วยให้คุณเพิ่มการใช้หน่วยความจำของ jvm เนื่องจากการสร้างวัตถุและโดยทั่วไปคือขนาดของวัตถุ


จะเกิดอะไรขึ้นถ้า GC ทำงานตรงกลางระหว่าง // รหัสสำหรับการสร้างวัตถุ ตอนนี้อาจให้ผลลัพธ์ที่ถูกต้องตลอดเวลา
rajugaadu

0

คำตอบนี้ไม่เกี่ยวข้องกับขนาดวัตถุ แต่เมื่อคุณใช้อาร์เรย์เพื่อรองรับวัตถุ ขนาดหน่วยความจำจะจัดสรรให้กับวัตถุเท่าใด

ดังนั้นอาร์เรย์รายการหรือแมปคอลเลกชันเหล่านั้นทั้งหมดจะไม่เก็บวัตถุจริงๆ (เฉพาะในช่วงเวลาที่จำเป็นต้องใช้ขนาดหน่วยความจำวัตถุจริง) เท่านั้นมันจะเก็บการอ้างอิงสำหรับวัตถุเหล่านั้นเท่านั้น

ตอนนี้ Used heap memory = sizeOfObj + sizeOfRef (* 4 bytes) in collection

  • (4/8 ไบต์) ขึ้นอยู่กับ (32/64 บิต) ระบบปฏิบัติการ

primitives

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

OBJECTS

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.

ฉันหมายถึงว่าวัตถุทั้งหมดที่อ้างอิงต้องการหน่วยความจำเพียง 4 ไบต์ มันอาจเป็นการอ้างอิงสตริงหรือการอ้างอิงวัตถุสองครั้ง แต่ขึ้นอยู่กับการสร้างวัตถุหน่วยความจำที่จำเป็นจะแตกต่างกันไป

เช่น) ถ้าฉันสร้างวัตถุสำหรับคลาสด้านล่างReferenceMemoryTestจะมีการสร้างหน่วยความจำ 4 + 4 + 4 = 12 ไบต์ หน่วยความจำอาจแตกต่างกันเมื่อคุณพยายามเริ่มต้นการอ้างอิง

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

ดังนั้นเมื่อมีการสร้างอาร์เรย์วัตถุ / อ้างอิงเนื้อหาทั้งหมดจะถูกครอบครองด้วยการอ้างอิงเป็นโมฆะ และเรารู้ว่าแต่ละการอ้างอิงต้องการ 4 ไบต์

และสุดท้ายการจัดสรรหน่วยความจำสำหรับโค้ดด้านล่างคือ 20 ไบต์

ReferenceMemoryTest ref1 = new ReferenceMemoryTest (); (4 (ref1) + 12 = 16 ไบต์) ReferenceMemoryTest ref2 = ref1; (4 (ref2) + 16 = 20 ไบต์)


1
จำนวนเต็ม 4 ไบต์และการอ้างอิงวัตถุที่มีขนาดที่ไม่รู้จักนั้นมีขนาด 4 ไบต์ได้อย่างไร?
มาร์ควิสแห่ง Lorne

@EJP ฉันหมายถึงว่าวัตถุอ้างอิงทั้งหมดต้องการหน่วยความจำเพียง 4 ไบต์ มันอาจเป็นการอ้างอิงสตริงหรือการอ้างอิงวัตถุสองครั้ง แต่ขึ้นอยู่กับการสร้างวัตถุหน่วยความจำที่จำเป็นจะแตกต่างกันไป
Kanagavelu Sugumar

0

สมมติว่าฉันประกาศชั้นเรียนชื่อComplex:

public class Complex {

    private final long real;
    private final long imaginary;

    // omitted
}

เพื่อดูจำนวนหน่วยความจำที่จัดสรรให้กับอินสแตนซ์สดของคลาสนี้:

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

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

-5

สำหรับ JSONObject รหัสด้านล่างสามารถช่วยคุณได้

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

ส่งคืนขนาดเป็นไบต์

ฉันตรวจสอบด้วยวัตถุ JSONArray ของฉันโดยเขียนลงในไฟล์ มันให้ขนาดวัตถุ


สิ่งนี้จะใช้ได้กับวัตถุที่เป็นสตริงเท่านั้น
เด็กซ์เตอร์ Legaspi

-6

ฉันสงสัยว่าคุณต้องการที่จะทำมันโดยทางโปรแกรมยกเว้นว่าคุณเพียงแค่ต้องการทำเพียงครั้งเดียวและเก็บไว้ใช้ในอนาคต มันเป็นสิ่งที่ต้องทำ ไม่มีตัวดำเนินการ sizeof () ใน Java และแม้ว่าจะมีก็จะนับเฉพาะค่าใช้จ่ายของการอ้างอิงไปยังวัตถุอื่นและขนาดของดั้งเดิม

วิธีหนึ่งที่คุณสามารถทำได้คือจัดเรียงสิ่งต่าง ๆ เป็นไฟล์และดูขนาดของไฟล์ดังนี้:

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

แน่นอนนี่ถือว่าแต่ละวัตถุนั้นแตกต่างกันและไม่มีการอ้างอิงที่ไม่ใช่สิ่งชั่วคราว

อีกกลยุทธ์หนึ่งคือการนำแต่ละวัตถุและตรวจสอบสมาชิกโดยการสะท้อนกลับและเพิ่มขนาด (boolean & byte = 1 byte, short & char = 2 bytes, ฯลฯ ) เพื่อลดลำดับชั้นของสมาชิก แต่นั่นน่าเบื่อและมีราคาแพงและจบลงด้วยการทำสิ่งเดียวกันกับกลยุทธ์การทำให้เป็นอันดับ


3
ฉันจะทำให้มันเป็นไบต์ [] โดยใช้ ByteArrayOutputStream มันจะเร็วกว่าการเขียนลงไฟล์เป็นอย่างมาก
ScArcher2

@KorayTugay การกำหนดขนาดไบต์ของวัตถุเป็นการดำเนินการที่มีค่าใช้จ่าย การเขียนแต่ละวัตถุลงบนดิสก์เพื่อกำหนดขนาดจะทำให้คลาน ...
HammerNL

1
รูปแบบของวัตถุที่ต่อเนื่องนั้นแตกต่างอย่างสิ้นเชิงกับรูปแบบของวัตถุในหน่วยความจำฮีป ที่สะดุดตาที่สุดคือ descriptor สำหรับคลาสของ object (และ superclasses ที่ต่อเนื่องกันทั้งหมด) ถูกเขียนไปที่ stream ดังนั้นการเขียนอินสแตนซ์อย่างง่ายของการjava.lang.Integerสร้างประมาณ 80 ไบต์ซึ่งโดยปกติแล้วการแทนฮีปคือ 32 (ซึ่งแตกต่างจากการแสดงสตรีมวัตถุการแทนฮีปขึ้นอยู่กับขนาดตัวชี้และการจัดเรียงวัตถุ) ในทางตรงกันข้ามการnullอ้างอิงแบบอนุกรมต้องใช้หนึ่งไบต์แทนสี่หรือแปดไบต์ในหน่วยความจำฮีป
Holger
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.