Bellek Kullanımı Analiz: Java vs C ++ İhmal Edilebilir?


9

Java ile yazılan bir tam sayı nesnesinin bellek kullanımı, C ++ ile yazılmış bir tam sayı nesnesinin bellek kullanımı ile nasıl karşılaştırılır? Fark ihmal edilebilir mi? Fark yok? Büyük bir fark? Aynı olduğunu düşünüyorum çünkü int ne olursa olsun int (?)

Bunu sormamın nedeni , bir programın bellek gereksinimlerinin programcının verilen bir sorunu çözmesini ne zaman engelleyeceğini bilmenin önemini okuyordum .

Beni büyüleyen tek bir Java nesnesi oluşturmak için gereken bellek miktarı. Örneğin, bir tam sayı nesnesini ele alalım. Yanılıyorsam ama bir Java tam sayı nesnesi 24 bayt bellek gerektiriyorsa beni düzeltin:

  • İnt örnek değişkeni için 4 bayt
  • 16 bayt ek yük (nesnenin sınıfına referans, çöp toplama bilgisi ve senkronizasyon bilgisi)
  • 4 bayt dolgu

Başka bir örnek olarak, bir Java dizisi (nesne olarak uygulanır) 48 + bayt gerektirir:

  • 24 bayt başlık bilgisi
  • 16 baytlık nesne yükü
  • Uzunluk için 4 bayt
  • Dolgu için 4 bayt
  • artı değerleri saklamak için gereken bellek

Bu bellek kullanımları C ++ ile yazılmış aynı kodla nasıl karşılaştırılır?

Eskiden yazdığım C ++ ve Java programlarının bellek kullanımından habersizdim, ama şimdi algoritmalar hakkında bilgi edinmeye başladığım için, bilgisayarın kaynakları için daha fazla takdir görüyorum.


6
C ++ 'da "tamsayı nesnesi" nedir? int? Öyleyse, C ++ girişleriniz 32 bit olduğu sürece intbunu Java ile karşılaştırmamalısınız Integer.
Mat

+1 Yalnızca tek bir int değişkeni olan bir c ++ sınıfı oluşturduysam, bunu somutlaştırdım
Anthony

3
int bir int değildir - platforma bağımlıdır

1
Yalnızca bir int üyesi olan bir C ++ genel olarak herhangi bir ek yüke sahip değildir. Platformun bir int depolamak için kullandığı alan kadar kullanır (genellikle mevcut PC platformlarında 4 bayt).
Dirk Holsopple

Bir Java programcısı için belleği merak eden +1. Her şeyden öte, bellek farkındalığı, modern mimarilerde performansı belirleyen en önemli faktördür.
imallett

Yanıtlar:


15

Platforma ve uygulamaya bağlıdır.

C ++, boyutunun chartam olarak bir bayt ve en az 8 bit genişliğinde olduğunu garanti eder . Daha sonra, a'nın boyutu short inten az 16 bittir ve daha küçük değildir char. Bir intbüyüklüğünün boyutu en az büyüklüğündedir short int. Boyutu long inten az 32 bit ve int'den daha küçük değil.

sizeof(char) == 1; sizeof(long int) >= sizeof(int) >= sizeof(short int) >= sizeof(bool) >= sizeof(char).

C ++ 'ın gerçek bellek modeli çok kompakt ve öngörülebilir . Örneğin, nesnelerde, dizilerde veya işaretçilerde meta veri yoktur. Yapılar ve sınıflar, diziler gibi bitişiktir, ancak gerekli ve ihtiyaç duyulan yerlerde dolgu yapılabilir.

Açıkçası, bu tür karşılaştırma en iyi aptalca çünkü Java bellek kullanımı, çalıştırılan koda göre Java uygulamasına daha fazla bağlıdır.


1
Doğru, ancak karşılaştırma uğruna, eşdeğer boyutlu tamsayı türlerini varsaymamız gerektiğini söyleyebilirim (yalnızca farklı adlar altında mevcut olsalar bile). Sonuçta, farklı boyutlar farklı semantik anlamına gelir ve birçok (hepsi değil) ortak platformlarda boyutlar aynıdır veya aynı boyutta tamsayılar farklı adlar altında kullanılabilir.

OP'ye not: Kendinizi tamsayı boyutunu seçmek daha iyi olabilir - C ++ 'da 32 bit int istiyorsanız, kullanabilirsiniz int32_t.
K.Steff

9

Cevapların çoğu birkaç önemli noktayı görmezden geliyor gibi görünüyor.

Birincisi, çok fazla Java'da, neredeyse hiç çiğ bir şey görmezsiniz int- neredeyse tüm kullanımları vardır Integer, bu yüzden intbir intC veya C ++ ile aynı boyutta (yaklaşık) olması neredeyse alakasızdır (bunun dışında) deneyimlerime göre, küçük) kod intyerine yalnızca kod yüzdesi Integer.

İkincisi, tek tek nesnelerin boyutlarının, bir bütün olarak bir programın bellek ayak izi ile hemen hemen hiçbir ilgisi yoktur. Java'da, programın bellek ayak izi öncelikle çöp toplayıcının nasıl ayarlandığı ile ilgilidir. Çoğu durumda, GC hızı en üst düzeye çıkarmak için ayarlanır, bu da (büyük ölçüde) GC'yi nadiren mümkün olduğunca çalıştırmak anlamına gelir.

Şu anda kullanışlı bir bağlantım yok, ancak Java'nın C ile aynı hızda çalışabileceğini gösteren bazı testler yapıldı, ancak bunu yapmak için GC'yi yaklaşık 7 kat daha fazla kullanacak kadar nadiren çalıştırmanız gerekiyor hafıza. Bunun nedeni, tek tek nesnelerin 7 kat daha büyük olmasından değil, GC çok sık yaparsanız oldukça pahalı olabileceğinden. Daha da kötüsü, GC yalnızca bir nesneye erişmenin artık bir yolu olmadığını, yalnızca onu kullandığınızı bildiğinizden ziyade "kanıtlayabildiği" zaman serbest bırakabilir. Bu, bellek kullanımını en aza indirmek için GC'yi daha sık çalıştırsanız bile, muhtemelen tipik bir programın daha büyük bir bellek alanına sahip olmasını planlayabileceğiniz anlamına gelir. Böyle bir durumda, faktörü 7 yerine 2 veya 3'e düşürebilirsiniz, ancak aşırı derecede denize düşseniz bile,1 .

Duruma bağlı olarak, önemli olabilecek veya olmayabilecek başka bir faktör daha vardır: JVM'nin işgal ettiği bellek. Bu az ya da çok sabittir, bu nedenle yüzde olarak, uygulamanın kendi depolama alanına ihtiyaç duymaması çok büyük olabilir veya uygulamanın çok fazla depolaması gerekiyorsa çok küçük olabilir. En azından makinemde, en önemsiz Java uygulaması bile 20-25 megabayt gibi bir şey işgal ediyor gibi görünüyor (trivlal programları için 1000x'in üzerinde veya büyük olanlar için neredeyse ölçülemeyecek kadar küçük olabilir).


1 Bu, hiç kimsenin Java'yı C ++ ile elde edeceğinize yakın bir ayak izi ile yazmayı başaramayacağı anlamına gelmez. Bu demek sadece var sadece nesnelerin aynı sayıda / boyutuna sahip ve orada bir kural olarak almazsınız gerçekten sık GC çalışan.


7
İlk noktayı ilgili olarak: Hiç Java biriyim ama Java API ben hiç kullanılmamış görüldü ettik Integer(niye istiyorsunuz?) Yerine int. Yalnızca genel koleksiyonların Integertür silme nedeniyle kullanmaktan başka seçeneği yoktur , ancak önemserseniz bunları özel bir uygulamaya intveya ihtiyacınız olan ilkel türe değiştirebilirsiniz. Ve sonra genel sarma kodundan geçmek için geçici bir boks var (örn Object[]. An gerektiren her şey ). Bunun dışında GC alanı yükü için kaynaklarınız var mı? Gerçekten şüphe etmiyorum, sadece merak ediyorum.


9

Umarım tüm bunlar Java ve C ++ için derinlemesine uygulama tanımlıdır. Bununla birlikte, Java'nın nesne modeli biraz yer gerektirir.

C ++ nesneleri (genellikle) üyelerin ihtiyacı dışında herhangi bir depolama gerekmez. (Kullanıcı tanımlı her şeyin bir referans türü olduğu Java'dan farklı olarak), istemci kodunun bir nesneyi hem değer türü hem de referans türü olarak kullanabileceği, yani bir nesnenin başka bir nesneye bir işaretçi / referans depolayabileceği veya nesneyi doğrudan depolayabileceğini unutmayın. dolaylı olmadan. Herhangi bir virtualyöntem varsa nesne başına ek bir işaretçi gereklidir , ancak polimorfizm olmadan geçinmek için birkaç yararlı sınıf tasarlanmıştır ve buna ihtiyaç yoktur. GC meta verisi ve nesne başına kilit yoktur. Böylece class IntWrapper { int x; public: IntWrapper(int); ... };nesneler düz ints'den daha fazla alana ihtiyaç duymaz ve koleksiyonlara ve diğer nesnelere doğrudan (yani dolaylı olarak) yerleştirilebilir.

Diziler basittir çünkü C ++ 'da bir Java Dizisi ile önceden hazırlanmış, ortak bir eşdeğer yoktur. Sadece bir grup nesneyi new[](kesinlikle ek yük / meta veri olmadan) tahsis edebilirsiniz, ancak uzunluk alanı yoktur - uygulama muhtemelen birini depolar, ancak erişemezsiniz. std::vectordinamik bir dizidir ve bu nedenle ek yük ve daha geniş bir arayüze sahiptir. std::arrayve C tarzı diziler (int arr[N];), derleme zamanı sabiti gerekir. Teoride, sadece nesnenin depolaması ve uzunluk için tek bir tamsayı olmalıdır - ancak dinamik yeniden boyutlandırma ve çok az ekstra alana sahip tam özellikli bir arayüz elde edebileceğiniz için, pratikte bunu tercih edersiniz. Tüm bunların yanı sıra diğer tüm koleksiyonların, nesneleri değere göre depolamak, böylece size dolaylı aktarım ve referanslar için yer tasarrufu sağlamak ve önbellek davranışını iyileştirmek olduğunu unutmayın. Dolaylı olarak almak için işaretçileri (akıllı olanlar, lütfen) açıkça saklamalısınız.

Yukarıdaki karşılaştırmalar tamamen adil değildir, çünkü bu tasarruflardan bazıları Java'nın içerdiği özellikler dahil edilmediğinden kaynaklanmaktadır ve C ++ eşdeğeri genellikle Java eşdeğerinden (*) daha az optimize edilmiştir. Yaygın yolu uygulamak için virtualtam olarak uygulamak için ortak bir yol olarak çok yük olarak C ++ uygular içinde virtualJava. Bir kilit almak için, büyük olasılıkla birkaç bitten daha büyük olan tam özellikli bir mutex nesnesine ihtiyacınız vardır. Referans sayımı almak için ( değilGC'ye eşdeğerdir ve bu şekilde kullanılmamalıdır, ancak bazen yararlıdır), bir referans sayım alanı ekleyen akıllı bir işaretçiye ihtiyacınız vardır. Nesne dikkatle oluşturulmadıkça, başvuru sayısı, akıllı işaretçi nesnesi ve başvurulan nesne tamamen ayrı konumlardadır ve doğru oluşturduğunuzda bile, paylaşılan işaretçi bir yerine iki işaretçi olabilir (gerekir mi?). Sonra tekrar, iyi C ++ stili önemli olan bu özellikleri kullanmaz - pratikte, iyi yazılmış bir C ++ kütüphanesinin nesneleri daha az kullanır. Yani yok illa genel ortalama az bellek kullanımı, ancak ortalama C ++ bu konuda iyi bir avantaj vardır yapıyor.

(*) Örneğin, tür bilgilerini çeşitli bayraklarla birleştirerek ve nesneler için kilit bitlerini kaldırarak sanal aramalar, kimlik karma kodları ve bazı nesneler için yalnızca bir sözcükle (ve diğer birçok nesne için iki sözcükle) kilitleme yapabilirsiniz. kilitlere ihtiyaç duymaz. Bu ve diğer optimizasyonların ayrıntılı açıklaması için bkz . Java Nesne Modelinin Alan ve Zaman Verimli Uygulaması (PDF), David F. Bacon, Stephen J. Fink ve David Grove.


3

Java'daki bir düz int, inther iki uygulamanın da aynı tamsayı boyutunu ve bellek hizalamasını kullanması şartıyla, C ++ 'da olduğu kadar yer kaplar .

Bir int 'nesnesi' ( kutulu bir tamsayı, yani Integerbir sınıf örneği), Java'daki bir sınıf örneğinin tüm ek yükünü taşır, bu nedenle intC ++ 'daki birinden daha büyüktür . Bununla birlikte, C ++ 'da bir nesneyi Java nesnelerinin hazır olduğu özelliklerle (polimorfizm, boks, çöp toplama, RTTI) aynı özelliklerle donatsaydınız, muhtemelen eşit bir nesneye sahip olursunuz. boyut.

Ve sonra optimizasyon hususları var; yürütme modelleri ve programlama paradigmaları farklı olduğundan, önemsiz olmayan herhangi bir sorunun her iki dilde de aynı şekilde çözülmesi olası değildir, bu nedenle bu boyuttaki depolama boyutunun karşılaştırılması inanılmaz bir anlam ifade etmez.

Evet, Java nesneleri varsayılan olarak C ++ sınıflarından daha fazla yük taşır, ancak daha fazla özellik ile gelirler ve bu farklı bir programlama stiline yol açar - iyi bir programcı her iki dilin yukarı ve aşağı taraflarından da yararlanabilir.


+1 Daha fazla genel gider ama Java'da daha fazla özellik, şimdi anladım, teşekkürler
Anthony
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.