في جافا، ما هي أفضل طريقة لتحديد حجم الكائن؟
سؤال
على سبيل المثال، لنفترض أن لدي تطبيقًا يمكنه القراءة في ملف 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 هذا؛ليس لديه تبعيات خارجية:
توفر الكثير من الإجابات الأخرى أحجامًا ضحلة - على سبيل المثال.حجم 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 بايت، وما إلى ذلك)، والعمل في طريقك إلى أسفل التسلسل الهرمي للعضوية.لكن هذا أمر ممل ومكلف وينتهي به الأمر بنفس الشيء الذي ستفعله استراتيجية التسلسل.