Java'da bir nesnenin boyutunu belirlemenin en iyi yolu nedir?


617

Veri satırları yığınları ile bir CSV dosyası okuyan bir uygulama var. Kullanıcıya veri türlerine göre satır sayısının bir özetini veriyorum, ancak çok fazla veri satırında okumadığımdan ve nedenlere neden olmadığından emin olmak istiyorum OutOfMemoryError. Her satır bir nesneye dönüşür. Bu nesnenin boyutunu programlı olarak bulmanın kolay bir yolu var mı? İlkel türlerin ve nesne başvurularının a için ne kadar büyük olduğunu tanımlayan bir başvuru var mı VM?

Şu anda, 32.000 satıra kadar okunan bir kod var , ancak 32MB bellek kullanana kadar mümkün olduğunca çok sayıda satır okuyan bir kod olmasını istiyorum . Belki bu farklı bir soru, ama yine de bilmek istiyorum.


Agent'ımı mvn yapılandırmalarıyla ekledim ve burada nasıl açıklandığını açıkladım: stackoverflow.com/a/36102269/711855
juanmf 19:16

Yanıtlar:


461

Java.lang.instrument paketini kullanabilirsiniz

Bu sınıfı derleyin ve bir JAR'a yerleştirin:

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

Aşağıdakileri kendinize ekleyin MANIFEST.MF:

Premain-Class: ObjectSizeFetcher

GetObjectSize kullanın:

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

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

Şununla ara:

java -javaagent:ObjectSizeFetcherAgent.jar C

2
@Stefan Güzel ipucu! Eğer büyüklüğünde olacak ne söyleyebilir byte[0], byte[1], byte[5], int[0], int[1], int[2]yaklaşımını kullanarak tarif? Sonuçlar dizi uzunluğu ve bellek hizalaması için ek yük içeriyorsa iyi olur.
dma_k

8
Bunu denedim ve garip ve yararsız sonuçlar aldım. Dizeler, boyutlarına bakılmaksızın her zaman 32 idi. Bunun işaretçi boyutu olduğunu düşündüm ama oluşturduğum başka bir değişmez sınıf için 24 var. İlkel için iyi çalışıyor ama sonra bir karakterin ne kadar büyük olduğunu söylemek için bir programa ihtiyacınız yok.
Brel

6
@Bu çözüm, dokümantasyonda belirtildiği gibi yalnızca "belirtilen nesne tarafından tüketilen depolama miktarının yaklaşık bir değeridir". Ayrıca yazarların bir String örneğinin paylaşıldığı (havuzda depolanan) veya yerel ve bir sınıfa özgü.
Andrei I

11
Jar dışa aktarmazsanız nasıl ObjectSizeFetcher kullanıcısı olabilir? Tutulmada test java projem var.
Yura Shinkarev

3
@brel Bir dizenin gerçek uzunluğa bakılmaksızın yalnızca 32 bayt olmasının nedeni, dizenin değişken uzunluk bölümünün kendi nesnesi olan bir char [] içinde depolanmasıdır. Bir nesnenin gerçek boyutunu elde etmek için, kendisinin boyutunu ve başvurduğu her nesnenin boyutunu eklemeniz gerekir.
tombrown52

117

OpenJDK projesinin bir parçası olarak geliştirilen jol'u kullanmalısınız .

JOL (Java Nesne Düzeni) JVM'lerde nesne düzeni şemalarını analiz etmek için küçük bir araç kutusudur. Bu araçlar, gerçek nesne düzenini, kapladığı alanı ve referansları kodunu çözmek için Güvenli Olmayan, JVMTI ve Servis Verilebilirlik Aracı (SA) kullanmaktadır. Bu, JOL'yi yığın dökümlerine, şartname varsayımlarına vb. Dayanan diğer araçlardan çok daha doğru hale getirir.

Temel öğelerin, referansların ve dizi öğelerinin boyutlarını almak için kullanın VMSupport.vmDetails(). 64 bit Windows üzerinde çalışan Oracle JDK 1.8.0_40'ta (aşağıdaki tüm örnekler için kullanılır), bu yöntem geri döner

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()(İsteğe bağlı olarak bir örneği geçirerek) kullanarak bir nesne örneğinin sığ boyutunu elde edebilirsiniz toPrintable. Bu sadece o sınıfın tek bir örneği tarafından tüketilen alandır; söz konusu sınıf tarafından başvurulan başka bir nesne içermez. Bu etmez nesne başlığı için VM yükünü alan hizalama ve koruyucular içerir. Şunun için 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

Düğmesini kullanarak bir nesne örneğinin derin boyutunun özet görünümünü alabilirsiniz GraphLayout.parseInstance(obj).toFootprint(). Tabii ki, ayak izindeki bazı nesneler paylaşılabilir (diğer nesnelerden de referans alınır), bu nedenle bu nesne çöp toplandığında geri kazanılabilecek alanın aşırı yakınlaştırılmasıdır. Sonuç olarak Pattern.compile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$")( bu cevaptan alınmıştır ), jol, toplam 1840 baytlık bir ayak izi rapor eder, bunlardan sadece 72'si Pattern örneğinin kendisidir.

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)

Bunun yerine GraphLayout.parseInstance(obj).toPrintable(), jol size başvurulan her nesne için adres, boyut, tür, değer ve alan dereferences yolunu söyleyecektir, ancak bu genellikle kullanışlı olmak için çok fazla ayrıntıdır. Devam eden model örneği için aşağıdakileri alabilirsiniz. (Adresler büyük olasılıkla çalıştırmalar arasında değişecektir.)

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)

"(Başka bir şey)" girdileri , öbekteki bu nesne grafiğinin parçası olmayan diğer nesneleri açıklar .

En iyi jol belgeleri, jol deposundaki jol örnekleridir . Örnekler ortak jol işlemlerini gösterir ve VM ve çöp toplayıcı iç bileşenlerini analiz etmek için jol'u nasıl kullanabileceğinizi gösterir.


18
Bu cevabın daha fazla oyu olmalı. Kesinlikle kontrol etmek için çok iyi bir seçenek. EDIT: '08 'de soru sorulurken bu yıl eklendiğini kontrol etti. Muhtemelen OP'nin şu anda sorduğu şeyi yapmak için en iyi ve en kolay seçenek.
kiralar

4
Araç yazarı Jol hakkında bir blog yazısı yazdı .
Mike

2
Object "obj" boyutunu belirlemek için şunu kullanın: org.openjdk.jol.info.GraphLayout.parseInstance (obj) .totalSize ();
canlılık

vmDetailsŞimdi not edin VM.current().details().
Miha_x64

Check out GraphLayout.parseInstance(instance).toFootprint()Ben daha yararlı nesne boyutlarını anlamak bulundu
Mugen

82

Yanlışlıkla jdk içinde, kullanımı kolay ve bir nesnenin boyutunu belirlemek için oldukça yararlı görünüyor bir java sınıfı "jdk.nashorn.internal.ir.debug.ObjectSizeCalculator", buldum.

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

Sonuçlar:

164192
48
16
48
416

3
Aynı şekilde, yukarıda önerilen ve ObjectSizeCalculator ile karşılaştığım diğer çözümleri deniyordum. Yakın zamanda Nashorn projesinin bir parçası olarak JDK 8'de tanıtıldığından beri kimsenin bahsetmediğine inanmıyorum . Ancak bu sınıfla ilgili resmi bir belge bulamadım.
Henrique Gontijo

Dize uzunluklarını dikkate almıyor gibi görünüyor. Yığındaki boyut hakkında mı?
jontejj

1
Bir hashmap var, burada com.carrotsearch.RamUsageEstimator ObjectSizeCalculator yaklaşık yarısını döndürür. Hangisi doğru? - Hangisi daha güvenilir?
badera

9
ObjectSizeCalculatorYalnızca HotSpot
VM'de

74

Birkaç yıl önce Javaworld'ün kompozit ve potansiyel olarak iç içe geçmiş Java nesnelerinin boyutunu belirleme üzerine bir makalesi vardı , temelde Java'da bir sizeof () uygulaması oluşturmaya çalışıyorlar. Yaklaşım temel olarak, insanların ilkel ve tipik Java nesnelerinin boyutunu deneysel olarak tanımladığı ve daha sonra bu bilgiyi, toplam boyutu hesaplamak için bir nesne grafiğini özyinelemeli yürüyen bir yönteme uyguladığı diğer çalışmalara dayanır.

Bir sınıfın perde arkasında olan şeyler nedeniyle, yerel C uygulamasından her zaman biraz daha az doğru olacaktır, ancak iyi bir gösterge olmalıdır.

Alternatif olarak, sizeof () uygulamasına sahip bir Java5 kitaplığı sunan sizeof adı verilen uygun bir SourceForge projesi .

PS Serileştirme yaklaşımını kullanmayın, serileştirilmiş bir nesnenin boyutu ile canlı olarak tükettiği bellek miktarı arasında bir ilişki yoktur.


6
Sizeof yardımcı programı muhtemelen en hızlı yoldur. Stefan'ın söylediği bu, ama zaten kullanıma hazır bir kavanozda paketlenmiş.
Alexandre L Telles

62

İlk olarak "bir nesnenin boyutu" Java'da iyi tanımlanmış bir kavram değildir. Nesnenin kendisi, yalnızca üyeleri, Nesne ve başvurduğu tüm nesneler (referans grafiği) ile ifade edebilirsiniz. Bellekteki veya diskteki boyut anlamına gelebilir. Ve JVM'nin Dizeler gibi şeyleri optimize etmesine izin verilir.

Bu yüzden tek doğru yol, JVM'ye, iyi bir profil oluşturucuyla ( YourKit kullanıyorum ) sormaktır , ki bu muhtemelen istediğiniz şey değildir.

Bununla birlikte, yukarıdaki açıklamadan, her satır kendi kendine yetecek ve büyük bir bağımlılık ağacına sahip olmayacak gibi görünüyor, bu nedenle serileştirme yöntemi muhtemelen çoğu JVM'de iyi bir yaklaşım olacaktır. Bunu yapmanın en kolay yolu aşağıdaki gibidir:

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

Eğer ortak referanslarla nesneler varsa bu unutmayın olmayacaktır doğru sonucu verir ve serileştirme büyüklüğü her zaman hafızada boyutuna uygun olmaz, ama iyi bir tahmindir. ByteArrayOutputStream boyutunu mantıklı bir değere ilklendirirseniz kod biraz daha verimli olacaktır.


2
Bu yaklaşımı seviyorum. Nesne boyutu açısından ne kadar uzakta olursunuz.
Berlin Brown

1
Çok basit ve etkili. Diğer yöntemler çok dağınıktır (özellikle Eclipse RCP içinde). Teşekkürler.
marcolopes

19
Serileştirme geçici değişkenleri izlemez ve varsayılan serileştirme yöntemi UTF-8'de dizeler yazar, bu nedenle ANSI karakterleri yalnızca bir bayt alır. Çok sayıda dizeniz varsa, boyutunuz işe yaramayacak kadar uzakta olacaktır.
TMN

1
Bu tam boyutu vermese de, ihtiyaçlarım için sadece 2 nesne ile bir karşılaştırmaya ihtiyacım vardı ve SizeOf bir web uygulamasından başlatılmayacak. Teşekkürler!
Isaac

1
YourKit'in iyi tavsiyesi . Diğer alternatifler VirtualVM ve jvmmonitor
angelcervera

38

JVM'nizde ne kadar bellek kullanıldığını ve ne kadar boş olduğunu bilmek istiyorsanız, şöyle bir şey deneyebilirsiniz:

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

edit: Soru yazar da "32MB bellek kullanana kadar mümkün olduğunca çok satır okumak" mantık olmasını istediğini belirtti gibi bu yararlı olabilir düşündüm.


24
Bir çöp toplama işleminin ne zaman gerçekleşeceğini veya bir kerede öbeğe ne kadar fazla bellek ayrılacağını asla bilemeyeceğiniz için bu iyi bir çözüm değildir.
Nick Fortescue

5
Bu doğrudur ve bunu bu yazının ana sorusunu ele almak niyetinde değilim, ancak maksimum yığın boyutuna çarpmaya yaklaştığında programlı olarak bilmesine yardımcı olabilir.
matt b

1
Bu çözümün diğer sorunu, çok iş parçacıklı bir ortamda (bir web sunucusunda olduğu gibi) olduğunuzdadır. Diğer iş parçacıklarının yürütülmesi ve bellek tüketmesi mümkündür. Bu yaklaşımla, tüm sanal makinede kullanılan belleği hesaplıyorsunuz.
angelcervera

1
Başka bir dezavantaj, freeMemory'nin bir yaklaşık değer döndürmesidir. Bir javax.crypto.Cipher nesnesi oluşturmayı deneyin. İki freeMemory çağrısı arasındaki fark (bir Şifrenin boyutunu tahmin etmek için) sabit değildir!
Eugen

1
Bir çöp koleksiyonunu zorlayabileceğinize inanıyorum, bu yüzden bu yaklaşımda bazı şeyler yapabilirsiniz .
matanster

24

Twitter'da çalışırken derin nesne boyutunu hesaplamak için bir yardımcı program yazdım. Farklı bellek modellerini (32 bit, sıkıştırılmış oops, 64 bit), dolgu, alt sınıf dolguları, dairesel veri yapıları ve dizileri üzerinde doğru şekilde çalışır. Sadece bu bir .java dosyasını derleyebilirsiniz; harici bağımlılıkları yoktur:

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


1
Szia! Sadece sunumunuzu da bağırmak istiyorum : 15-20 slaytları, çeşitli veri yapısı kararlarının maliyeti için içgüdüsel bir fikir edinmenize yardımcı olmak için harika. Gönderdiğiniz için teşekkürler!
Luke Usherwood

16
"dışsal bağımlılığı yoktur" - çünkü guava ne zaman dışsal bağımlılık değildir?
l4mpi


Guave harici bir bağımlılıktır.
Mert Serimer

18

Diğer cevapların çoğu sığ boyutlar sağlar - örneğin herhangi bir anahtar veya değer içermeyen bir HashMap boyutu, ki bu muhtemelen istediğiniz bir şey değildir.

Jamm projesi yukarıdaki java.lang.instrumentation paketini kullanır ancak ağacı yürür ve böylece size derin bellek kullanımı sağlayabilir.

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm

MemoryMeter kullanmak için JVM'yi "-javaagent: /jamm.jar" ile başlatın.


11

Yansıma kullanarak nesneleri yürümek zorunda. Yaparken dikkatli ol:

  • Sadece bir nesnenin tahsis edilmesi JVM'de bir miktar ek yüke sahiptir. Miktar JVM'ye göre değişir, bu nedenle bu değeri bir parametre yapabilirsiniz. En azından sabit (8 bayt?) Yapın ve tahsis edilen her şeye uygulayın.
  • Çünkü byteteorik olarak 1 bayt belleğe sadece birini alır anlamına gelmez.
  • Nesne referanslarında döngüler olacaktır, bu nedenle sonsuz döngüleri ortadan kaldırmak için karşılaştırıcı olarak nesne-eşitlerini kullanarak a HashMapveya daha fazlasını tutmanız gerekir .

@jodonnell: Çözümünüzün sadeliğini seviyorum, ancak birçok nesne Seri hale getirilemez (bu nedenle bir istisna atar), alanlar geçici olabilir ve nesneler standart yöntemleri geçersiz kılabilir.


Java Spesifikasyonunda tanımlanan çeşitli ilkellerin boyutları değil mi? (§2.4.1)
erickson

4
"Ne kadar bellek kaplıyor" anlamında değil. Sadece nasıl çalıştıkları anlamında. Örneğin, bayt, karakter ve şort, yuvarlama vb.İle çalışsalar da, Java yığınında tüm kelimeyi kaplarlar.
Jason Cohen

1
Heinz tarafından Bülten # 78'de gösterildiği gibi, boyutun ölçülmesine benzer: javaspecialists.eu/archive/Issue078.html . Onu kullandım. Yaklaşımı işe yarıyor.
Peter Kofler

8

Bir araçla ölçmeniz veya elle tahmin etmeniz gerekir ve bu, kullandığınız JVM'ye bağlıdır.

Nesne başına sabit bir ek yük vardır. JVM'ye özgüdür, ancak genellikle 40 bayt tahmin ederim. O zaman sınıfın üyelerine bakmalısın. Nesne başvuruları, 32 bit (64 bit) JVM'de 4 (8) bayttır. İlkel türler:

  • boolean ve bayt: 1 bayt
  • karakter ve kısa: 2 bayt
  • int ve float: 4 bayt
  • uzun ve çift: 8 bayt

Diziler aynı kuralları izler; yani, bir nesne referansıdır, böylece nesnenizde 4 (veya 8) bayt alır ve ardından uzunluğu öğesinin boyutuyla çarpılır.

Runtime.freeMemory()Çöp toplayıcısına eşzamansız çağrılar vb. Nedeniyle programlı çağrılarla yapmaya çalışmak size çok fazla doğruluk vermez. Yığın -Xrunhprof veya diğer araçlarla profillenmesi size en doğru sonuçları verecektir.


@erickson sizeof (boolean) == 1 bu konuya ( stackoverflow.com/questions/1907318/… ) bakarak emin değilim . Bu konuda yorum yapabilir misiniz?
dma_k

2
@dma_k, Java'nın aslında gerçek booleanları yoktur. Boolean boyutu diziler dışında 4 bayt ve içinde 1 bayttır boolean[]. Aslında çift / uzun olmayan tüm ilkeller 4 bayttır. İkincisi 8 (cevap yanlış onları 4 olarak koyar)
bestsss

@bestsss: Daha kesin olmak gerekirse, minimum bellek ayırma, JVM'nin platformuna ve uygulanmasına bağlıdır. Öbek üzerindeki nesneler de hizalanır, bu nedenle tüm boyutları topladıktan sonra yuvarlanması gerekir.
dma_k

6

java.lang.instrument.InstrumentationSınıf bir Java Nesne boyutunu almak için güzel bir yol sağlar, ama bir tanımlamanızı gerektirir premainve bir java ajan ile programınızı çalıştırın. Bu, herhangi bir ajana ihtiyacınız olmadığında çok sıkıcıdır ve daha sonra uygulamanıza bir kukla Jar ajanı sağlamanız gerekir.

Bu yüzden ' Unsafeden sınıfı kullanarak alternatif bir çözüm buldum sun.misc. Böylece, nesnelerin işlemci mimarisine göre hizalamalarını dikkate alarak ve maksimum alan ofsetini hesaplayarak, bir Java Nesnesinin boyutunu ölçebilirsiniz. Aşağıdaki örnekte UtilUnsafe, sun.misc.Unsafenesneye başvuru almak için yardımcı bir sınıf kullanıyorum .

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

İlginç bir yaklaşım, ancak bu, nesnenin ve alanlarının depolanmasının parçalanmadığını varsayar mı?
nicoulaj

Evet ve bu tür parçalanmalara neden olan herhangi bir JVM uygulaması bilmiyorum.
Miguel Gamboa

Anlamıyorum. Parçalanma bir seçenek değildir :) A ve B nesneleri alanı olarak saklanan C nesnesinin örneğini ele alalım. Her şeyi A veya B'de kaydırmıyor mu?
nicoulaj

Üzgünüm, ama ben de sizin bakış açınızı anlamıyorum. Benim yorumuma göre, Java nesnelerinde .Net'teki C yapılarında veya Değer Türlerinde olduğu gibi diğer nesnelerin içinde saklanamaz. Yani, “A ve B nesnelerinin alanı olarak saklanan C nesnesi” dediğinizde, A ve B nesnelerinin C nesnesine referansları (işaretçiler) depolayan alanları olduğu anlamına gelir. Daha sonra A ve B boyutu eşittir bu alanın ofseti artı C nesnesine bir referansın (işaretçi) boyutu ve bir referansın boyutu bir kelimenin boyutudur.
Miguel Gamboa

Oh, tamam, sığ boyuttan bahsediyoruz. Benim hatam.
nicoulaj

6

Aynı zamanda, benzer bir soruda tartışıldığı gibi basit ve ticari dostu Apache 2.0 lisansı altında yayınlanan Bellek Ölçer aracı da (eski adıyla GitHub'da Google Code'da mevcuttu ) var .

Bellek bayt tüketimini ölçmek istiyorsanız, java yorumlayıcısına bir komut satırı argümanı gerektirir, ancak aksi takdirde en azından kullandığım senaryolarda gayet iyi çalışıyor gibi görünüyor.


4

Enstrümantasyonla uğraşmak zorunda kalmadan ve bir nesnenin bayt-tam boyutunu bilmeniz gerekmiyorsa, aşağıdaki yaklaşımla gidebilirsiniz:

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

do your job here

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

Bu şekilde kullanılan belleği önce ve sonra okursunuz ve kullanılan belleği almadan hemen önce GC'yi çağırırsanız, "gürültüyü" neredeyse 0'a düşürürsünüz.

Daha güvenilir bir sonuç için işinizi n kez çalıştırabilir ve sonra kullanılan hafızayı n'ye bölerek, bir işlemin ne kadar bellek harcadığını elde edebilirsiniz. Dahası, her şeyi daha fazla çalıştırabilir ve ortalama yapabilirsiniz.


5
System.gc()Sadece GC yapmak istediğinizi bildirmekle kalmaz ? GC'nin hiç çağrıldığı garanti edilmez.
Raildex

@gerçekten hoş. Bu güvenli değildir, çünkü GC'nin hatlarınız arasındaki belleği asla etkilemeyeceği veya etkilemeyeceği anlamına gelmez. Yani iki arasında "freeMemory yöntemi" GC sizin düşünmediğiniz daha fazla alan açarak nesnenizin daha küçük görünmesini sağlar
Mert Serimer

@MertSerimer "güvenli değil" benim için tamamen farklı bir seviyede: en fazla da belirttiğim gibi, bu çok doğru değil. Ayrıca, GC'yi (Raildex'in belirttiği gibi) kullanamazsınız, ancak bu durumda da bunu bir döngüye eklemeyi önerdim. Bu, sonucun belirtildiği gibi çok güvenilir olması gerekmiyorsa çalışan hızlı ve kirli ve yaklaşık bir sistemdir.
reallynice

Bununla ilgili birçok sorun var ama size iyi bir yağma veriyor.
markthegrea

3

Sıkıştırılmış OOP ile 32 bit, 64 bit ve 64 bit işlemek için bağlantılı örneklerden bazılarını kullanarak yaptığım bir yardımcı program. Kullanır sun.misc.Unsafe.

Bu kullanan Unsafe.addressSize()bir yerli göstericinin ve boyutunu almak için Unsafe.arrayIndexScale( Object[].class )bir Java referans boyutu için.

Bir nesnenin taban boyutunu bulmak için bilinen bir sınıfın alan ofsetini kullanır.

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

Bu sınıfı değerlerle test ettiniz mi? Denedim, ama benim için yanlış değerler !!!
Débora

1
Basit bir nesne için bana verdiği değerler doğruydu, ancak 1mio nesneleri içeren bir liste için 10 faktörü vardı. Yine de, çok güzel bir iş!
Michael Böckling

İlginç. Windows 7 x64 ve Linux 2.6.16 / x86_64 üzerinde, 32bit / 64bit / oop adres modlarının her birini kullanarak JDK7u67 kullanarak test ettim. Eclipse Memory Analyzer 1.3.x'te analiz edilen bellek dökümleriyle karşılaştırdım. Hangi kurulumu kullanıyorsunuz? Deneyebileceğim belirli bir örnek var mı?
dlaudams

Yapabileceğim en iyi seçim. Ben cant'kullanma Instrumentationi tomcat'ı başlatmayın nedeniyle, ObjectSizeCalculatorçünkü emin VM türü (HotSpot) ve JOLbacouse bahar fasulye. Bunu kullanın ve Class ve Enum yoksaymak için singletons viz AbstractRefreshableApplicationContext.getBeanFactory().getSingletonMutex()ve refactor internalSizeOfkod yoksaymak için ikinci parametre eklemek
Perlos

Sonuçları karşılaştırmak için ObjectSizeCalculator kullanın (Tüm sunucuyu 1GB ile 10s arasında hesaplayın). JOL neden MemError (6GB enaught değil) ve ben aynı sonuçları, muhtemelen enums çünkü alamadım.
Perlos

3

Aşağıdaki gereksinimleri karşılayan bir nesne boyutu bir çalışma zamanı hesaplaması arıyordu:

  • Enstrümantasyona gerek kalmadan çalışma zamanında mevcuttur.
  • Güvenli Olmayan'a erişimi olmayan Java 9+ ile çalışır.
  • Yalnızca Sınıfa dayanmaktadır. Dize uzunluklarını, dizi uzunluklarını vb. Dikkate alan derin bir boyut değil.

Aşağıdakiler, orijinal java uzmanları makalesinin ( https://www.javaspecialists.eu/archive/Issue078.html ) temel koduna ve bu soruya verilen başka bir cevapta Güvenli olmayan sürümden birkaç bite dayanmaktadır.

Umarım birisi faydalı bulur.

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

Bir yöntem çağrısı yok, eğer istediğin buysa. Biraz araştırma yaparak sanırım kendi yazınızı yazabilirsiniz. Belirli bir örnek, referans sayısı ve ilkel değerlerin yanı sıra örnek defter tutma verilerinden türetilen sabit bir boyuta sahiptir. Sadece nesne grafiğini yürüyecektiniz. Sıra türleri ne kadar az çeşitlilik gösterirse, o kadar kolay olur.

Bu çok yavaş veya değerinden daha fazla sorun varsa, her zaman iyi eski moda satır sayma kuralı sayma vardır.


2

Anında tahmin etmek için bir kez hızlı bir test yazdım:

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

}

Genel kavram, nesneleri ayırmak ve serbest yığın alanındaki değişimi ölçmektir. Anahtar varlık getFreeMemory(), stabilize etmek için rapor serbest yığın boyutu için GC çalışır ve bekler talep . Yukarıdakilerin çıktısı:

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

Bu, hizalama davranışı ve olası yığın blok üstbilgisi yükü verildiğinde beklediğimiz şeydir.

Kabul edilen cevapta ayrıntılı olarak verilen enstrümantasyon yöntemi burada en doğru. Açıkladığım yöntem doğrudur, ancak başka hiçbir iş parçacığının nesne oluşturmadığı / atmadığı kontrollü koşullar altında.


2

Java visual VM kullanın.

Bellek sorunlarını profillemek ve hata ayıklamak için ihtiyacınız olan her şeye sahiptir.

Ayrıca, biri yararlı olan birçok yararlı şey yapmanıza izin veren bir OQL (Nesne Sorgulama Dili) konsolu vardır. sizeof(o)


2

JetBrains IntelliJ kullanırken, önce Dosya | Ayarlar | Oluşturma, Yürütme, Dağıtım | Debugger.

Hata ayıklama sırasında, ilgilenilen bir değişkeni sağ tıklayın ve "Tutulan Boyutu Hesapla" yı seçin: Tutulan Boyutu Hesapla


1

Cevabım Nick tarafından sağlanan koda dayanmaktadır. Bu kod, serileştirilmiş nesne tarafından kullanılan toplam bayt miktarını ölçer. Yani bu aslında serileştirme şeylerini + düz nesne bellek ayak izini ölçer (sadece serileştirin intve toplam serileştirilmiş bayt miktarının olmadığını göreceksiniz 4). Dolayısıyla, nesneniz için tam olarak kullanılan ham bayt numarasını almak istiyorsanız - bu kodu biraz değiştirmeniz gerekir. Şöyle ki:

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

}

Bu çözümü ilkel tiplerle, String ve bazı önemsiz sınıflarla test ettim. Kapsanan durumlar da olmayabilir.


GÜNCELLEME: Örnek, dizi nesnelerinin bellek ayak izi hesaplamasını desteklemek için değiştirildi.


0

Bir yığın dökümü (örneğin, jmap ile) oluşturabilir ve sonra nesne boyutlarını bulmak için çıktıyı analiz edebilirsiniz. Bu çevrimdışı bir çözümdür, ancak sığ ve derin boyutları vb. İnceleyebilirsiniz.


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

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

size, nesne oluşturma nedeniyle jvm'nin bellek kullanımında artış sağlar ve bu genellikle nesnenin boyutudur.


GC, // Nesne oluşturma kodu sırasında ortada çalışırsa ne olur? Şimdi her zaman doğru sonuç verebilir.
rajugaadu

0

Bu yanıt, Nesne boyutu ile ilgili değildir, ancak nesneleri barındırmak için dizi kullandığınızda; nesne için ne kadar bellek boyutu ayıracağını.

Bu nedenle, diziler, liste veya harita tüm bu koleksiyon nesneleri gerçekten depolamayacak (sadece ilkel zamanlar, gerçek nesne bellek boyutu gerekli), sadece bu nesneler için referansları saklayacaktır.

Şimdi Used heap memory = sizeOfObj + sizeOfRef (* 4 bytes) in collection

  • (4/8 bayt) (32/64 bit) işletim sistemine bağlıdır

primitifleri

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

NESNELER

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.

Demek istediğim, REFERENCE nesnesinin sadece 4 bayt belleğe ihtiyacı var. Dize başvurusu VEYA Çift nesne başvurusu olabilir, ancak nesne oluşturmaya bağlı olarak gereken bellek değişecektir.

eg) Eğer aşağıdaki sınıf için nesne oluşturursam ReferenceMemoryTest4 + 4 + 4 = 12 bayt bellek oluşturulacaktır. Referansları başlatmaya çalıştığınızda bellek farklı olabilir.

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

Nesne / referans dizisi oluştururken, tüm içeriği NULL referanslarla dolu olacaktır. Ve her bir referansın 4 bayt gerektirdiğini biliyoruz.

Ve son olarak, aşağıdaki kod için bellek ayırma 20 bayttır.

ReferenceMemoryTest ref1 = yeni ReferenceMemoryTest (); (4 (ref1) + 12 = 16 bayt) ReferenceMemoryTest ref2 = ref1; (4 (ref2) + 16 = 20 bayt)


1
4 baytlık bir tamsayı ve boyutu bilinmeyen bir nesne başvurusu 4 bayta nasıl sığar?
Lorne Marquis

@EJP REFERENCE nesnesinin sadece 4 bayt belleğe ihtiyacı olduğunu söylemek istiyorum. Dize başvurusu VEYA Çift nesne başvurusu olabilir, ancak nesne oluşturulmasına bağlı olarak gereken bellek değişecektir.
Kanagavelu Sugumar

0

Diyelim ki şöyle bir sınıf beyan ederim Complex:

public class Complex {

    private final long real;
    private final long imaginary;

    // omitted
}

Bu sınıfın canlı örneklerine ne kadar bellek ayrıldığını görmek için:

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

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

-5

JSONObject için aşağıdaki kod size yardımcı olabilir.

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

bayt cinsinden boyut döndürür

Bir dosyaya yazarak JSONArray nesnemle kontrol ettim. Nesne boyutu veriyor.


bu yalnızca çoğunlukla dize olan nesneler için geçerlidir.
Dexter Legaspi

-6

Sadece bir kez yapmak ve gelecekteki kullanım için saklamak istemiyorsanız programlı olarak yapmak istediğinizden şüpheliyim. Yapması pahalı bir şey. Java'da sizeof () operatörü yoktur ve olsa bile, yalnızca diğer nesnelere yapılan başvuruların maliyetini ve ilkellerin boyutunu sayar.

Bunu yapmanın bir yolu, bir dosyayı bir dosyaya serileştirmek ve dosyanın boyutuna bakmaktır, şöyle:

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

Tabii ki, bu her nesnenin farklı olduğunu ve başka hiçbir şeye geçici olmayan referanslar içermediğini varsayar.

Başka bir strateji, her nesneyi alıp üyelerini yansıtarak incelemek ve boyutları (boolean byte = 1 byte, short & char = 2 byte, vb.) Toplayarak, üyelik hiyerarşisinde aşağı inerek çalışmak olacaktır. Ama bu sıkıcı ve pahalı ve serileştirme stratejisinin yapacağı şeyi yapıyor.


3
Ben ByteArrayOutputStream kullanarak bir bayt [] için seri hale. Bir dosyaya yazmaktan çok daha hızlı olurdu.
ScArcher2

@KorayTugay Bir nesnenin bayt boyutunu belirlemek zaten maliyetli bir işlemdir. Boyutu belirlemek için her nesneyi diske yazmak, sadece tarama yapacak ...
HammerNL

1
Serileştirilmiş nesne biçimi, nesnenin yığın belleğindeki biçiminden tamamen farklıdır. En önemlisi, nesnenin sınıfı (ve tüm serileştirilebilir üst sınıfları) için bir tanımlayıcı akışa yazılır. Öyleyse java.lang.Integer, yığın temsilinin genellikle 32 olduğu basit bir örnek 80 civarında bayt üretir (nesne akışı gösteriminin aksine, yığın gösterimi işaretçi boyutlarına ve nesne hizalamasına bağlıdır). Buna karşılık, serileştirilmiş bir nullreferans yığın belleğindeki dört veya sekiz bayt yerine bir bayt gerektirir.
Holger
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.