في جافا، ما هي أفضل طريقة لتحديد حجم الكائن؟

StackOverflow https://stackoverflow.com/questions/52353

  •  09-06-2019
  •  | 
  •  

سؤال

على سبيل المثال، لنفترض أن لدي تطبيقًا يمكنه القراءة في ملف CSV يحتوي على أكوام من صفوف البيانات.أقدم للمستخدم ملخصًا لعدد الصفوف بناءً على أنواع البيانات، ولكن أريد التأكد من أنني لا أقرأ في عدد كبير جدًا من صفوف البيانات وأتسبب في ذلك OutOfMemoryErrorس.يترجم كل صف إلى كائن.هل هناك طريقة سهلة لمعرفة حجم هذا الكائن برمجياً؟هل هناك مرجع يحدد حجم الأنواع البدائية ومراجع الكائنات لـ a VM?

حاليًا، لدي رمز يقول "اقرأ حتى". 32000 صف, ، ولكني أرغب أيضًا في الحصول على رمز ينص على قراءة أكبر عدد ممكن من الصفوف حتى أستخدمها 32 ميجابايت من الذاكرة.ربما هذا سؤال مختلف، ولكن ما زلت أود أن أعرف.

هل كانت مفيدة؟

المحلول

يمكنك استخدام ال حزمة 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

نصائح أخرى

يجب عليك استخدام jol, ، وهي أداة تم تطويرها كجزء من مشروع OpenJDK.

JOL (Java Object Layout) هو صندوق أدوات صغير لتحليل مخططات تخطيط الكائنات في JVMs.تستخدم هذه الأدوات 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 لرأس الكائن ومحاذاة الحقل والحشو.ل 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().بالطبع، قد تتم مشاركة بعض الكائنات الموجودة في البصمة (يتم الرجوع إليها أيضًا من كائنات أخرى)، لذلك يعد هذا تقريبًا زائدًا للمساحة التي يمكن استعادتها عندما يتم جمع البيانات المهملة لهذا الكائن.للحصول على نتيجة Pattern.compile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$") (مأخوذ من هذه الإجابة) ، يُبلغ jol عن مساحة إجمالية تبلغ 1840 بايت، منها 72 بايت فقط هي مثيل النمط نفسه.

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 لتحليل الأجهزة الداخلية ومجمع البيانات المهملة.

قبل بضع سنوات كان لدى Javaworld مقالة عن تحديد حجم كائنات Java المركبة والمتداخلة المحتملة, ، فهم يقومون بشكل أساسي بإنشاء تطبيق sizeof() في Java.يعتمد هذا النهج بشكل أساسي على أعمال أخرى حيث حدد الأشخاص بشكل تجريبي حجم العناصر الأولية وكائنات Java النموذجية ثم قاموا بتطبيق تلك المعرفة على طريقة تقوم بشكل متكرر بتشغيل الرسم البياني للكائن لحساب الحجم الإجمالي.

سيكون دائمًا أقل دقة إلى حد ما من تطبيق لغة C الأصلي وذلك ببساطة بسبب الأشياء التي تحدث خلف الكواليس في الفصل الدراسي ولكن يجب أن يكون مؤشرًا جيدًا.

وبدلاً من ذلك، يُطلق على مشروع SourceForge اسم مناسب حجم يقدم مكتبة Java5 مع تطبيق sizeof().

ملاحظة.لا تستخدم أسلوب التسلسل، فلا يوجد ارتباط بين حجم الكائن المتسلسل وحجم الذاكرة التي يستهلكها عندما يكون مباشرًا.

أولاً، "حجم الكائن" ليس مفهومًا محددًا جيدًا في Java.يمكن أن تقصد الكائن نفسه، مع أعضائه فقط، والكائن وجميع الكائنات التي يشير إليها (الرسم البياني المرجعي).يمكن أن تقصد الحجم الموجود في الذاكرة أو الحجم الموجود على القرص.ويُسمح لـ JVM بتحسين أشياء مثل السلاسل.

لذا فإن الطريقة الصحيحة الوحيدة هي أن تطلب من JVM استخدام ملف تعريف جيد (أستخدمه يوركيت)، وهو على الأرجح ليس ما تريده.

ومع ذلك، من الوصف أعلاه يبدو أن كل صف سيكون مستقلاً بذاته، ولن يحتوي على شجرة تبعية كبيرة، لذلك من المحتمل أن تكون طريقة التسلسل تقريبًا جيدًا لمعظم أجهزة JVM.أسهل طريقة للقيام بذلك هي كما يلي:

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

تذكر أنه إذا كان لديك كائنات ذات مراجع مشتركة فهذا سوف لن إعطاء النتيجة الصحيحة، ولن يتطابق حجم التسلسل دائمًا مع الحجم في الذاكرة، ولكنه تقريب جيد.سيكون الكود أكثر كفاءة قليلاً إذا قمت بتهيئة حجم ByteArrayOutputStream إلى قيمة معقولة.

لقد وجدت بطريق الخطأ فئة Java "jdk.nashorn.internal.ir.debug.objectsizeizeCalculator" ، بالفعل في 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

إذا كنت ترغب فقط في معرفة مقدار الذاكرة المستخدمة في 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();

يحرر:أعتقد أن هذا قد يكون مفيدًا حيث ذكر مؤلف السؤال أيضًا أنه يود الحصول على منطق يتعامل مع "قراءة أكبر عدد ممكن من الصفوف حتى أستخدم 32 ميجا بايت من الذاكرة."

عندما كنت أعمل في تويتر، كتبت أداة لحساب حجم الكائن العميق.يأخذ في الاعتبار نماذج الذاكرة المختلفة (32 بت، عفوًا مضغوطًا، 64 بت)، الحشو، حشو الفئات الفرعية، ويعمل بشكل صحيح على هياكل البيانات الدائرية والمصفوفات.يمكنك فقط تجميع ملف .java هذا؛ليس لديه تبعيات خارجية:

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

توفر الكثير من الإجابات الأخرى أحجامًا ضحلة - على سبيل المثال.حجم HashMap بدون أي من المفاتيح أو القيم، وهو ما لا تريده على الأرجح.

يستخدم مشروع Jamm حزمة java.lang.instrumentation المذكورة أعلاه ولكنه يمشي على الشجرة وبالتالي يمكن أن يمنحك استخدامًا عميقًا للذاكرة.

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm

لاستخدام MemoryMeter، ابدأ تشغيل JVM باستخدام "-javaagent:/jamm.jar"

عليك أن تمشي الأشياء باستخدام الانعكاس.كن حذرا كما تفعل:

  • مجرد تخصيص كائن له بعض الحمل في JVM.يختلف المبلغ حسب JVM لذا يمكنك جعل هذه القيمة معلمة.على الأقل اجعله ثابتًا (8 بايت؟) وقم بتطبيقه على أي شيء مخصص.
  • فقط لأن byte من الناحية النظرية 1 بايت لا يعني أنه يأخذ بايت واحد فقط في الذاكرة.
  • ستكون هناك حلقات في مراجع الكائنات، لذا ستحتاج إلى الاحتفاظ بـ HashMap أو شيء من هذا القبيل باستخدام كائن يساوي كمقارنة للقضاء على الحلقات اللانهائية.

@جودونيل:تعجبني بساطة الحل الذي تقدمه، لكن العديد من الكائنات غير قابلة للتسلسل (لذلك قد يؤدي هذا إلى حدوث استثناء)، ويمكن أن تكون الحقول عابرة، ويمكن للكائنات تجاوز الأساليب القياسية.

يجب عليك قياسه باستخدام أداة، أو تقديره يدويًا، ويعتمد ذلك على JVM الذي تستخدمه.

هناك بعض الحمل الثابت لكل كائن.إنه خاص بـ JVM، لكنني عادةً ما أقدر 40 بايت.ثم عليك أن تنظر إلى أعضاء الفصل.مراجع الكائنات هي 4 (8) بايت في JVM 32 بت (64 بت).الأنواع البدائية هي:

  • منطقية وبايت:1 بايت
  • حرف وقصيرة:2 بايت
  • كثافة العمليات وتعويم:4 بايت
  • طويلة ومزدوجة:8 بايت

المصفوفات تتبع نفس القواعد؛أي أنه مرجع كائن بحيث يأخذ 4 (أو 8) بايت في الكائن الخاص بك، ثم يتم ضرب طوله بحجم عنصره.

تحاول القيام بذلك برمجياً من خلال المكالمات إلى Runtime.freeMemory() فقط لا يمنحك الكثير من الدقة، بسبب المكالمات غير المتزامنة إلى أداة تجميع البيانات المهملة، وما إلى ذلك.سيمنحك تصنيف الكومة باستخدام -Xrunhprof أو أدوات أخرى النتائج الأكثر دقة.

ال java.lang.instrument.Instrumentation يوفر class طريقة رائعة للحصول على حجم كائن 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() { }
}

وهناك أيضا مقياس الذاكرة أداة (سابقا في كود جوجل, ، من الآن جيثب)، وهو بسيط ويتم نشره تحت عنوان تجاري سهل الاستخدام ترخيص أباتشي 2.0, ، كما نوقش في أ سؤال مماثل.

ويتطلب أيضًا وسيطة سطر أوامر لمترجم Java إذا كنت تريد قياس استهلاك بايت الذاكرة، ولكن بخلاف ذلك يبدو أنه يعمل بشكل جيد، على الأقل في السيناريوهات التي استخدمتها.

فيما يلي أداة مساعدة قمت بإنشائها باستخدام بعض الأمثلة المرتبطة للتعامل مع 32 بت و64 بت و64 بت باستخدام OOP المضغوط.يستخدم sun.misc.Unsafe.

يستخدم Unsafe.addressSize() للحصول على حجم المؤشر الأصلي و Unsafe.arrayIndexScale( Object[].class ) لحجم مرجع جافا.

يستخدم إزاحة الحقل لفئة معروفة لحساب الحجم الأساسي للكائن.

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

دون الاضطرار إلى العبث بالأجهزة وما إلى ذلك، وإذا لم تكن بحاجة إلى معرفة الحجم الدقيق للبايت لكائن ما، فيمكنك اتباع النهج التالي:

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

do your job here

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

بهذه الطريقة يمكنك قراءة الذاكرة المستخدمة قبل وبعد، واستدعاء GC قبل الحصول على الذاكرة المستخدمة مباشرة، مما يؤدي إلى خفض "الضوضاء" إلى 0 تقريبًا.

للحصول على نتيجة أكثر موثوقية، يمكنك تشغيل مهمتك n مرات، ثم تقسيم الذاكرة المستخدمة على n، للحصول على مقدار الذاكرة التي تستغرقها عملية تشغيل واحدة.علاوة على ذلك، يمكنك تشغيل الأمر برمته مرات أكثر وتحقيق المتوسط.

لا يوجد استدعاء للطريقة، إذا كان هذا هو ما تطلبه.مع القليل من البحث، أفترض أنه يمكنك كتابة ما تريده بنفسك.مثيل معين له حجم ثابت مشتق من عدد المراجع والقيم الأولية بالإضافة إلى بيانات مسك الدفاتر للمثيل.يمكنك ببساطة السير على الرسم البياني للكائن.كلما كانت أنواع الصفوف أقل تنوعًا، كانت أسهل.

إذا كان ذلك بطيئًا للغاية أو مجرد مشكلة أكثر مما يستحق، فهناك دائمًا قواعد جيدة لعد الصفوف القديمة.

لقد كتبت اختبارًا سريعًا مرة واحدة لتقديره بسرعة:

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

وهو ما نتوقعه، نظرًا لسلوك المحاذاة والحمل المحتمل لرأس كتلة الكومة.

طريقة الأجهزة المفصلة في الإجابة المقبولة هنا هي الأكثر دقة.الطريقة التي وصفتها دقيقة ولكن فقط في ظل ظروف خاضعة للرقابة حيث لا توجد سلاسل رسائل أخرى تقوم بإنشاء/تجاهل الكائنات.

ما عليك سوى استخدام Java Visual VM.

إنه يحتوي على كل ما تحتاجه لملف تعريف مشاكل الذاكرة وتصحيحها.

كما أن لديها وحدة تحكم OQL (لغة استعلام الكائنات) التي تتيح لك القيام بالعديد من الأشياء المفيدة، أحدها sizeof(o)

تعتمد إجابتي على الكود الذي قدمه نيك.يقيس هذا الرمز إجمالي عدد البايتات التي يشغلها الكائن المتسلسل.لذلك فإن هذا يقيس فعليًا عناصر التسلسل + بصمة ذاكرة الكائن العادي (فقط قم بالتسلسل على سبيل المثال 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;
    }    

}

لقد اختبرت هذا الحل مع أنواع بدائية، وسلسلة، وعلى بعض الفئات التافهة.قد لا تكون هناك حالات مغطاة أيضا.


تحديث: تم تعديل المثال لدعم حساب أثر الذاكرة لكائنات المصفوفة.

يمكنك إنشاء تفريغ كومة الذاكرة المؤقتة (باستخدام jmap، على سبيل المثال) ثم تحليل الإخراج للعثور على أحجام الكائنات.هذا حل غير متصل بالإنترنت، ولكن يمكنك فحص الأحجام الضحلة والعميقة، وما إلى ذلك.

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

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

يمنحك الحجم زيادة في استخدام ذاكرة jvm بسبب إنشاء الكائن وهذا عادةً هو حجم الكائن.

هذه الإجابة لا تتعلق بحجم الكائن، ولكن عندما تستخدم المصفوفة لاستيعاب الكائنات؛مقدار حجم الذاكرة الذي سيتم تخصيصه للكائن.

لذا فإن المصفوفات أو القائمة أو الخريطة لن تقوم بتخزين كل هذه المجموعة الكائنات حقًا (فقط في وقت البدائيات، هناك حاجة إلى حجم ذاكرة الكائن الحقيقي)، بل ستقوم فقط بتخزين المراجع لتلك الكائنات.

الآن Used heap memory = sizeOfObj + sizeOfRef (* 4 bytes) in collection

  • (4/8 بايت) يعتمد على نظام التشغيل (32/64 بت).

البدائيون

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

أشياء

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

لذا، عند إنشاء مصفوفة كائن/مرجع، ستكون جميع محتوياتها مشغولة بمراجع NULL.ونحن نعلم أن كل مرجع يتطلب 4 بايت.

وأخيرًا، تخصيص الذاكرة للكود أدناه هو 20 بايت.

ReferenceMemoryTest ref1 = new ReferenceMemoryTest();(4 (Ref1) + 12 = 16 Bytes) ReferenceMomoryTest Ref2 = Ref1 ؛( 4(ref2) + 16 = 20 بايت)

لنفترض أنني أعلنت فئة اسمها 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

بالنسبة لـ JSONObject يمكن أن يساعدك الكود أدناه.

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

إرجاع الحجم بالبايت

لقد قمت بفحصه باستخدام كائن JSONArray الخاص بي عن طريق كتابته في ملف.إنه يعطي حجم الكائن.

أشك في أنك تريد القيام بذلك برمجيًا إلا إذا كنت تريد القيام بذلك مرة واحدة فقط وتخزينه لاستخدامه في المستقبل.إنه أمر مكلف للقيام به.لا يوجد عامل sizeof() في Java، وحتى لو كان موجودًا، فإنه سيحسب فقط تكلفة المراجع إلى الكائنات الأخرى وحجم العناصر الأولية.

إحدى الطرق التي يمكنك من خلالها القيام بذلك هي إجراء تسلسل للشيء في ملف وإلقاء نظرة على حجم الملف، مثل هذا:

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

بالطبع، يفترض هذا أن كل كائن مميز ولا يحتوي على مراجع غير عابرة لأي شيء آخر.

تتمثل الإستراتيجية الأخرى في أخذ كل كائن وفحص أعضائه من خلال التفكير وإضافة الأحجام (منطقي & بايت = 1 بايت، قصير & شار = 2 بايت، وما إلى ذلك)، والعمل في طريقك إلى أسفل التسلسل الهرمي للعضوية.لكن هذا أمر ممل ومكلف وينتهي به الأمر بنفس الشيء الذي ستفعله استراتيجية التسلسل.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top