Java'da bir nesnenin bellek tüketimi nedir?


216

100 özniteliğe sahip bir nesne tarafından tüketilen bellek alanı, her biri bir özniteliğe sahip 100 nesneninkiyle aynı mıdır?

Bir nesne için ne kadar bellek ayrılmıştır?
Bir özellik eklerken ne kadar ek alan kullanılır?

Yanıtlar:


180

Mindprod , bunun cevaplanması kolay bir soru olmadığını belirtiyor:

Bir JVM, verileri ilkel olarak resmi boyutlara sahip gibi davranmak zorunda olsa da, herhangi bir miktarda dolgu veya ek yük ile dahili, büyük veya küçük endiandan istediği şekilde depolamakta özgürdür.
Örneğin, JVM veya yerel derleyici boolean[]64 bit uzunluğunda bir a BitSet. Program aynı cevapları verdiği sürece size söylemek zorunda değildir.

  • Yığında bazı geçici Nesneler ayırabilir.
  • Bazı değişkenleri veya yöntem çağrılarını tamamen sabit olarak değiştirerek varlığını tamamen optimize edebilir.
  • Her biri belirli bir durum için optimize edilmiş yöntemlerin veya döngülerin sürümlerini, yani bir yöntemin iki sürümünü derleyebilir, sonra hangisini çağıracağınıza karar verebilir.

Daha sonra elbette donanım ve işletim sistemi, yonga önbellek, SRAM önbellek, DRAM önbellek, sıradan RAM çalışma seti ve diskte yedekleme deposu üzerinde çok katmanlı önbelleklere sahiptir. Verileriniz her önbellek düzeyinde çoğaltılabilir. Tüm bu karmaşıklık RAM tüketimini ancak çok kabaca tahmin edebileceğiniz anlamına gelir.

Ölçüm yöntemleri

Instrumentation.getObjectSize()Bir nesne tarafından tüketilen depolama alanı tahminini elde etmek için kullanabilirsiniz .

Gerçek nesne düzenini, kapladığı alanı ve referansları görselleştirmek için JOL (Java Nesne Düzeni) aracını kullanabilirsiniz .

Nesne başlıkları ve Nesne başvuruları

Modern bir 64 bit JDK'da, bir nesnenin 8 baytın katlarına doldurulmuş 12 baytlık bir başlığı vardır, bu nedenle minimum nesne boyutu 16 bayttır. 32 bit JVM'ler için ek yük 8 bayttır ve 4 baytın katlarına doldurulur. ( Dmitry Spikhalskiy'nin cevabından , Jayen'in cevabından ve JavaWorld'den .)

Tipik olarak, referanslar 32 bit platformlarda veya 64 bit platformlarda 4 bayttır -Xmx32G; ve 32Gb ( -Xmx32G) üzerinde 8 bayt . ( Sıkıştırılmış nesne referanslarına bakın .)

Sonuç olarak, 64 bit JVM tipik olarak% 30-50 daha fazla yığın alanı gerektirir. ( 32 veya 64 bit JVM kullanmalı mıyım?, 2012, JDK 1.7)

Kutulu türler, diziler ve dizeler

Kutulu sarmalayıcılar, ilkel türlere ( JavaWorld'den ) göre ek yüke sahiptir :

  • Integer: 16 baytlık sonuç beklediğimden biraz daha kötü çünkü bir intdeğer sadece 4 ekstra bayta sığabiliyor. Bir kullanarak Integerhavai ben ilkel türü olarak değerini saklayabilir zaman kıyasla bana yüzde 300 bellek maliyeti

  • Long: 16 bayt ayrıca: Yığındaki gerçek nesne boyutu, belirli bir CPU türü için belirli bir JVM uygulaması tarafından yapılan düşük düzeyli bellek hizalamasına tabidir. Görünüşe göre a Long, 8 bayt Object ek yükü, artı gerçek uzun değer için 8 bayt daha fazla. Buna karşılık, Integerkullanılmayan 4 baytlık bir delik vardı, çünkü JVM I, 8 baytlık bir kelime sınırında nesne hizalamasını zorlar.

Diğer konteynerler de pahalıdır:

  • Çok boyutlu diziler : başka bir sürpriz sunuyor.
    Geliştiriciler genellikle int[dim1][dim2]sayısal ve bilimsel hesaplama gibi yapılar kullanırlar .

    Bir int[dim1][dim2]dizi örneğinde, her iç içe int[dim2]dizi Objectkendi başına birdir. Her biri normal 16 baytlık dizi yükünü ekler. Üçgen veya düzensiz bir diziye ihtiyacım olmadığında, bu saf yükü temsil eder. Dizi boyutları büyük ölçüde farklı olduğunda etki büyür.

    Örneğin, bir int[128][2]örnek 3.600 bayt alır. Bir int[256]örneğin kullandığı (aynı kapasiteye sahip) 1.040 bayt ile karşılaştırıldığında , 3.600 bayt,% 246 ek yükü temsil eder. Aşırı durumda byte[256][1], genel faktör neredeyse 19'dur! Bunu, aynı sözdiziminin herhangi bir depolama ek yükü eklemediği C / C ++ durumuyla karşılaştırın.

  • String: Stringa'nın bellek büyümesi dahili karakter dizisinin büyümesini izler. Ancak, Stringsınıf 24 baytlık bir ek yük daha ekler.

    String10 karakter veya daha küçük bir boşluk için, faydalı yüke göre eklenen genel maliyet (her karakter için 2 bayt ve uzunluk için 4 bayt), yüzde 100 ila 400 arasındadır.

hizalanma

Şu örnek nesneyi düşünün :

class X {                      // 8 bytes for reference to the class definition
   int a;                      // 4 bytes
   byte b;                     // 1 byte
   Integer c = new Integer();  // 4 bytes for a reference
}

Saf bir miktar, bir örneğinin X17 bayt kullanacağını gösterir. Bununla birlikte, hizalama nedeniyle (dolgu olarak da adlandırılır), JVM belleği 8 baytın katları olarak ayırır, böylece 17 bayt yerine 24 bayt tahsis eder.


int [128] [6]: 6 inç - toplam 768 in, 12872 veri + 2064 bayt 128 diziler Nesne yükü = 5166 bayt toplam. int [256]: Toplam 256 int - dolayısıyla karşılaştırılamaz. int [768]: 3072 bayt veri + 16 byes yükü - 2B dizinin alanının yaklaşık 3 / 5'i -% 246 ek yükü değil!
JeeBee

Ah, orijinal makale int [128] [6] değil int [128] [2] kullandı - bunun nasıl değiştiğini merak ediyorum. Ayrıca aşırı örneklerin farklı bir hikaye anlatabileceğini gösterir.
JeeBee

2
64bit JVM'lerde yük 16 bayttır.
Tim Cooper

3
@AlexWien: Bazı çöp toplama şemaları dolgudan ayrı minimum bir nesne boyutu uygulayabilir. Çöp toplama sırasında, bir nesne eski bir konumdan yeni bir konuma kopyalandığında, eski konumun nesne için verileri tutması gerekmeyebilir, ancak yeni konuma bir referans tutması gerekir; ayrıca, ilk referansın keşfedildiği ve bu referansın eski nesne içindeki ofsetinin bulunduğu eski nesneye bir referans saklaması gerekebilir [çünkü eski nesne hala işlenmemiş referanslar içerebilir].
supercat

2
@AlexWien: Çöp toplayıcının kitap tutma bilgilerini tutmak için bir nesnenin eski konumunda bellek kullanmak, bu amaçla başka bellek ayırma ihtiyacını ortadan kaldırır, ancak aksi takdirde gerekenden daha büyük bir minimum nesne boyutu dayatabilir. Bence en az bir .NET çöp toplayıcı sürümü bu yaklaşımı kullanır; bazı Java çöp toplayıcılarının da bunu yapması kesinlikle mümkün olacaktır.
supercat

34

Bu mimari / jdk'ye bağlıdır. Modern bir JDK ve 64 bit mimarisi için, bir nesnenin 12 bayt üstbilgisi ve 8 bayt dolgusu vardır - bu nedenle minimum nesne boyutu 16 bayttır. Bir boyut belirlemek ve herhangi bir varlığın nesne düzeni ve iç yapısı hakkında ayrıntılar almak için Java Nesne Düzeni adlı bir araç kullanabilirsiniz veya bu bilgileri sınıf referansıyla tahmin edebilirsiniz. Ortamımda Tamsayı için çıktı örneği:

Running 64-bit HotSpot VM.
Using compressed oop with 3-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]

java.lang.Integer object internals:
 OFFSET  SIZE  TYPE DESCRIPTION                    VALUE
      0    12       (object header)                N/A
     12     4   int Integer.value                  N/A
Instance size: 16 bytes (estimated, the sample instance is not available)
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

Bu nedenle, Tamsayı için örnek boyutu 16 bayttır, çünkü 4 bayt int üstbilgiden hemen sonra ve dolgu sınırından önce sıkıştırılmıştır.

Kod örneği:

import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.util.VMSupport;

public static void main(String[] args) {
    System.out.println(VMSupport.vmDetails());
    System.out.println(ClassLayout.parseClass(Integer.class).toPrintable());
}

Javen kullanıyorsanız, JOL almak için:

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.3.2</version>
</dependency>

28

Her nesnenin ilişkili monitör ve tür bilgileri ile alanların kendileri için belirli bir yükü vardır. Bunun ötesinde, alanlar hemen hemen düzenlenebilir, ancak JVM uygun görür (inanıyorum) - ancak başka bir cevapta gösterildiği gibi , en azından bazı JVM'ler oldukça sıkı bir şekilde paketlenecektir. Bunun gibi bir sınıf düşünün:

public class SingleByte
{
    private byte b;
}

vs

public class OneHundredBytes
{
    private byte b00, b01, ..., b99;
}

32 bitlik bir JVM'de 100 örnek SingleByte1200 bayt almayı beklerdim (dolgu / hizalama nedeniyle alan için 8 bayt ek yük + 4 bayt). Bir örneğinin OneHundredBytes108 bayt almasını beklerdim - tepegöz ve sonra 100 bayt paketlendi. Yine de JVM'ye göre değişebilir - bir uygulama, alanları paketlememeye karar verebilir OneHundredBytes, bu da 408 bayt (= 8 bayt ek yükü + 4 * 100 hizalanmış / yastıklı bayt) almasına neden olabilir. 64 bit JVM'de ek yük de daha büyük olabilir (emin değilim).

DÜZENLEME: Aşağıdaki yoruma bakın; görünüşe göre HotSpot, 32 yerine 8 bayt sınırına ulaşır, bu nedenle her örneği SingleByte16 bayt alır.

Her iki durumda da, "tek büyük nesne" en azından birden fazla küçük nesne kadar verimli olacaktır - bunun gibi basit durumlar için.


9
Aslında, bir SingleByte örneği, Sun JVM'de 16 bayt, yani 8 bayt ek yük, 4 bayt alan ve daha sonra nesne dolgusu için 4 bayt
alacaktı

6

Bir programın toplam kullanılan / boş belleği programda

java.lang.Runtime.getRuntime();

Çalışma zamanının bellekle ilgili çeşitli yöntemleri vardır. Aşağıdaki kodlama örneği kullanımını göstermektedir.

package test;

 import java.util.ArrayList;
 import java.util.List;

 public class PerformanceTest {
     private static final long MEGABYTE = 1024L * 1024L;

     public static long bytesToMegabytes(long bytes) {
         return bytes / MEGABYTE;
     }

     public static void main(String[] args) {
         // I assume you will know how to create a object Person yourself...
         List < Person > list = new ArrayList < Person > ();
         for (int i = 0; i <= 100000; i++) {
             list.add(new Person("Jim", "Knopf"));
         }
         // Get the Java runtime
         Runtime runtime = Runtime.getRuntime();
         // Run the garbage collector
         runtime.gc();
         // Calculate the used memory
         long memory = runtime.totalMemory() - runtime.freeMemory();
         System.out.println("Used memory is bytes: " + memory);
         System.out.println("Used memory is megabytes: " + bytesToMegabytes(memory));
     }
 }

6

Her nesnenin 32 bit sistemlerde (ve 64 bit sistemlerde 24 bayt) 16 baytlık bir ek yükü olduğu görülmektedir.

http://algs4.cs.princeton.edu/14analysis/ iyi bir bilgi kaynağıdır. Çok iyi olanlardan bir örnek aşağıdadır.

resim açıklamasını buraya girin

http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf de çok bilgilendiricidir, örneğin:

resim açıklamasını buraya girin


"Her nesnenin 32 bit sistemlerde (ve 64 bit sistemlerde 24 bayt) 16 bayt ek yükü olduğu görülüyor." En azından mevcut JDK'lar için doğru değil. Benim bir göz atın cevap Tamsayı örneğin. Nesnenin yükü, 64 bit sistem ve modern JDK için başlık için en az 12 bayttır. Dolgu nedeniyle daha fazla olabilir, nesnedeki alanların gerçek düzenine bağlıdır.
Dmitry Spikhalskiy

Bellek verimli Java eğitimine ikinci bağlantı ölü gibi görünüyor - "Yasak".
tsleyson

6

100 özniteliğe sahip bir nesne tarafından tüketilen bellek alanı, her biri bir özniteliğe sahip 100 nesneninkiyle aynı mıdır?

Hayır.

Bir nesne için ne kadar bellek ayrılmıştır?

  • Tepegöz 32 bit üzerinde 8 bayt, 64 bit üzerinde 12 bayttır; ve daha sonra 4 bayt (32 bit) veya 8 baytın (64 bit) katlarına yuvarlanır.

Bir özellik eklerken ne kadar ek alan kullanılır?

  • Öznitelikler 1 bayt (bayt) ile 8 bayt (uzun / çift) arasında değişir, ancak başvurular 32 bit veya 64 bit olmasına bağlı olarak değil 4-bayt veya 8 bayttır , aksine -Xmx <32Gb veya> = 32Gb: tipik 64 -bit JVM'ler, yığın 32Gb'nin altındaysa referansları 4 bayta sıkıştıran "-UseCompressedOops" adlı bir optimizasyona sahiptir.

1
bir karakter 8 bit değil 16 bittir.
comonad

haklısın. Birisi orijinal cevabımı düzenledi gibi görünüyor
Jayen

5

Hayır, bir nesnenin kaydedilmesi biraz hafıza gerektirir. 1 özniteliğe sahip 100 nesne daha fazla bellek kaplar.


4

Soru çok geniş olacak.

Sınıf değişkenine bağlıdır veya java'da bellek kullanımını bildirebilirsiniz.

Ayrıca, üstbilgiler ve referanslar için bazı ek bellek gereksinimleri vardır.

Bir Java nesnesi tarafından kullanılan yığın belleği şunları içerir:

  • ilkel alanlar için boyutlarına göre bellek (ilkel tiplerin boyutları için aşağıya bakınız);

  • referans alanları için bellek (her biri 4 bayt);

  • birkaç byte "temizlik" bilgisinden oluşan bir nesne başlığı;

Java'daki nesneler ayrıca, bir nesnenin sınıfını, kimliğini ve nesnenin o anda erişilebilir olup olmadığını, şu anda senkronizasyon kilitli olup olmadığını gibi durum bayraklarını kaydetme gibi bazı "temizlik" bilgileri gerektirir.

Java nesnesi başlık boyutu 32 ve 64 bit jvm'ye göre değişir.

Bunlar ana bellek tüketicileri jvm olmasına rağmen, bazen kodun hizalanması vb. Gibi ek alanlar gerektirir.

İlkel tiplerin boyutları

boole ve bayt - 1

karakter ve kısa - 2

int ve şamandıra - 4

uzun ve çift - 8


: Okuyucular da bu kağıdı çok aydınlatıcı bulabilir cs.virginia.edu/kim/publicity/pldi09tutorials/...
quellish



1

hayır, 100 küçük nesnenin büyük olandan daha fazla bilgiye (belleğe) ihtiyacı vardır.


0

Ne kadar bellek tüketildiğine ilişkin kurallar JVM uygulamasına ve CPU mimarisine bağlıdır (örneğin 32 bit - 64 bit).

SUN JVM ile ilgili ayrıntılı kurallar için eski bloguma bak

Saygılarımızla, Markus


Eminim Sun Java 1.6 64bit, düz bir nesne için 12 bayt gerekir + 4 dolgu = 16; bir Nesne + bir tamsayı alanı = 12 + 4 = 16
AlexWien

Blogunuzu kapattınız mı?
Johan Boulé

Pek değil, SAP bloglarının bir şekilde taşındığından emin değilim. Çoğu burada bulunabilir kohlerm.blogspot.com
kohlerm
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.