Java yığın ve yığın bellek


99

Anladığım kadarıyla, Java'da yığın belleği ilkelleri tutar ve yöntem çağrıları yapar ve yığın belleği nesneleri depolamak için kullanılır.

Diyelim ki bir sınıfım var.

class A {
       int a ;
       String b;
       //getters and setters
}
  1. aSınıftaki ilkel nerede Asaklanacak?

  2. Yığın belleği neden hiç yok? Neden her şeyi yığında saklayamıyoruz?

  3. Nesne çöp toplandığında, nesne ile ilişkili yığın imha edilir mi?


1
stackoverflow.com/questions/1056444/is-it-on-the-stack-or-heap sorunuza cevap veriyor gibi görünüyor.
S.Lott

@ S.Lott, bunun Java ile ilgili olması dışında, C değil.
Péter Török

@ Péter Török: Kabul etti. Kod örneği Java olsa da, yalnızca Java olduğunu gösteren bir etiket yoktur. Genel prensip Java'ya C kadar iyi uygulanmalı. Ayrıca, bu sorunun Yığın Taşması ile ilgili birçok cevabı var.
S.Lott

9
@SteveHaigh: Bu sitede, herkes bir şeylerin buraya ait olup olmadığından çok fazla endişe duyuyor ... Acaba bu sitenin gerçekten soruları buraya ait olup olmadığına dair tüm nitelikleri ne şekilde aldığını merak ediyorum.
Sam Goldberg

Yanıtlar:


106

Yığın ve yığın arasındaki temel fark, değerlerin yaşam döngüsüdür.

Yığın değerleri yalnızca oluşturuldukları işlevin kapsamında bulunur. Döndükten sonra atılırlar.
Ancak yığın değerleri yığın üzerinde bulunur. Zaman içinde bir noktada yaratılırlar ve bir başlarına imha edilirler (dile veya çalışma zamanına bağlı olarak GC veya manuel olarak).

Şimdi Java, yalnızca ilkelleri yığında depolar. Bu, yığını küçük tutar ve ayrı yığın karelerini küçük tutmaya yardımcı olarak daha fazla iç içe arama yapılmasına izin verir.
Yığın üzerinde nesneler oluşturulur ve yalnızca referanslar (sırayla ilkel olan) yığın üzerinde iletilir.

Böylece, bir nesne yaratırsanız, fonksiyon çağrısı geri döndükten sonra da devam edebilmesi için kendisine ait tüm değişkenlerle birlikte yığına konur.


2
"ve sadece referanslar (sırayla ilkeldir)" Neden referansların ilkel olduğunu söylesin? Lütfen açıklayabilir misin?
Geek

5
@Geek: Çünkü ilkel veri tiplerinin ortak tanımı geçerlidir: "bir programlama dili tarafından temel bir yapı taşı olarak sağlanan bir veri tipi" . Ayrıca, referansların makalenin ilerisindeki kanonik örnekler arasında listelendiğini fark edebilirsiniz .
back2dos

4
@Geek: Veri açısından, ilkel veri türlerinden herhangi birini (referanslar dahil) sayı olarak görüntüleyebilirsiniz. Hatta chars sayılardır ve birbirleri yerine kullanılabilir. Referanslar ayrıca 32 veya 64 bit uzunluğunda bir bellek adresine atıfta bulunan numaralardır (bununla birlikte kullanılamazlar - ancak bununla uğraşmazsanız sun.misc.Unsafe).
Sune Rasmussen

3
Bu cevabın terminolojisi yanlıştır. Java Dil Belirtimi'ne göre, referanslar ilkel değildir. Cevabın söylediklerinin özü yine de doğrudur. (. Eğer referanslar "bir anlamda" olduğunu bir tartışma yapmak mümkün olmakla birlikte ilkel JLS Java için terminolojiyi tanımlayan saldırıdaki tarafından olduğunu ve ilkel türleri olduğunu söylüyor boolean, byte, short, char, int, long, floatve double.)
Stephen C

4
İçinde bulunan gibi, bu maddeye , bir Java olabilir yığında nesne (veya hatta küçük kısa ömürlü nesneler için yazmaçlarda) depolar. JVM kapakların altında biraz yapabilir. "Şimdi Java yalnızca yığında ilkelleri depolar" demek tam olarak doğru değildir.

49

İlkel alanlar nerede depolanır?

İlkel alanlar, bir yerde başlatılan nesnenin bir parçası olarak depolanır . Bunun nerede olduğunu düşünmenin en kolay yolu - öbek. Ancak , bu her zaman böyle değildir. Java teorisi ve pratiğinde tanımlandığı gibi : Kentsel performans efsaneleri, tekrar gözden geçirildi :

JVM'ler, kaçış analizi adı verilen bir tekniği kullanabilirler; bu sayede belirli nesnelerin tüm ömürleri boyunca tek bir dişe sınırlı kaldığını ve bu ömrün belirli bir yığın çerçevesinin ömrü ile sınırlandırıldığını söyleyebilirler. Bu tür nesneler yığın yerine istif üzerinde güvenle tahsis edilebilir. Daha da iyisi, küçük nesneler için, JVM tahsisi tamamen ortadan kaldırabilir ve nesnenin alanlarını kayıtlara kaldırabilir.

Bu nedenle, "nesne oluşturulur ve alan da oradadır" derken, öbek üzerinde veya yığında bir şey olup olmadığı söylenemez. Küçük, kısa ömürlü nesneler için, 'nesnenin' bu şekilde bellekte kalmayacağını ve bunun yerine alanlarını doğrudan kayıt defterlerine yerleştirebileceğini unutmayın.

Rapor şununla sona erer:

JVM'ler, yalnızca geliştiricinin bildiğini varsayarsak kullandığımız şeyleri bulmakta şaşırtıcı derecede iyidir. JVM'nin yığın tahsisi ve yığın tahsisi arasında durum bazında seçmesine izin vererek, programcının yığında mı yoksa yığın üzerinde mi dağıtılacağı konusunda agonize etmesine gerek kalmadan yığın tahsisinin performans avantajlarını elde edebiliriz.

Dolayısıyla, şuna benzeyen bir kodunuz varsa:

void foo(int arg) {
    Bar qux = new Bar(arg);
    ...
}

nerede ...izin vermez quxo kapsamını bırakmak, qux olabilir yerine yığın tahsis edilecek. Bu aslında VM için bir kazançtır, çünkü bunun hiç bir zaman çöp toplanması gerekmediği anlamına gelir - kapsamdan çıktığında kaybolacaktır.

Wikipedia'da kaçış analizi hakkında daha fazla bilgi . Gazetelere dalmaya istekli olanlar için , IBM'den Java için Escape Analysis . C # dünyasından gelenler için Yığın Bir Uygulama Ayrıntısı ve Eric Lippert tarafından Değer Türleri Hakkında Gerçeği iyi okumalar bulabilirsiniz (Java kavramlarında çoğu kavram ve özellik aynı veya benzer olduğu için faydalıdır) . Neden .Net kitaplar yığın vs yığın bellek ayırma hakkında konuşurlar? ayrıca buna da giriyor.

Yığın ve yığın yığınında

Öbek üzerinde

Öyleyse, neden yığın ya da öbek yok? Kapsam bırakan şeyler için yığın pahalı olabilir. Kodu düşünün:

void foo(String arg) {
    bar(arg);
    ...
}

void bar(String arg) {
    qux(arg);
    ...
}

void qux(String arg) {
    ...
}

Parametreler de yığının bir parçasıdır. Bir öbeğiniz olmadığı durumda, yığında tüm değer kümesini geçiyor olmalısınız. Bu "foo"küçük ve küçük dizeler için gayet iyi ... ama birisi o dizgeye büyük bir XML dosyası koyarsa ne olur. Her arama dev dizenin tamamını yığına kopyalar - ve bu çok israf olur.

Bunun yerine, yaşamı biraz fazla olan nesnelerin hemen kapsamın dışına (başka bir kapsama geçtiniz, başka birinin koruduğu bir yapıya sıkışmış vb.) Dışa yığın adı verilen bir alana koymak daha iyidir.

Yığında

Sen yok gerek yığını. Bir varsayımsal olarak, bir yığın (keyfi derinlikte) kullanmayan bir dil yazabilir. Gençliğimde öğrendiğim eski bir BASIC bunu yaptı, biri yalnızca 8 seviye gosubçağrı yapabiliyordu ve tüm değişkenler globaldi - yığın yoktu.

Yığının avantajı, bir kapsamla var olan bir değişkeniniz olduğunda, o kapsamdan ayrıldığınızda, o yığın çerçevesinin fırlatılmasıdır. Orada olanı ve olmayanı gerçekten kolaylaştırıyor. Program başka bir işleme, yeni bir yığın çerçeveye geçer; program işleme geri döner ve mevcut kapsamınızı gören programa geri dönersiniz; Program prosedürü terk eder ve istifin üzerindeki tüm öğeler serbest bırakılır.

Bu, kodun bir yığın ve bir yığın kullanması için çalışma zamanını yazan kişinin hayatı kolaylaştırır. Bunlar sadece kod üzerinde çalışarak birçok kişi ve kodu dilde yazan kişinin açık bir şekilde düşünmekten kurtulmasına izin veren birçok yöntem ve yöntemdir.

Yığının doğası da parçalanamayacağı anlamına gelir. Bellek parçalanması öbek ile ilgili gerçek bir sorundur. Birkaç nesne atarsınız, sonra çöpler bir tane toplar ve sonra bir sonraki büyükler için yer bulmayı deneyin. Onun bir karmaşa. Bir şeyleri yığına koyabilmek, bununla uğraşmak zorunda olmadığınız anlamına gelir.

Bir şey çöp toplandığında

Bir şey çöp toplanınca gitmiş demektir. Ancak sadece toplanan çöpler çünkü zaten unutulmuş durumda - programdaki mevcut programdan erişilebilen nesneye daha fazla referans yok.

Bunun, çöp toplama işleminin çok büyük bir sadeleştirme olduğuna dikkat çekeceğim. Çeşitli bayrakları (kullanarak çöp toplamayı çimdik - Orada bile Java içinde birçok çöp toplayıcılar (olan docs ) Bu farklı davranır ve her biri şeyler yapar nasıl nüansları Bu yanıt için biraz çok derin Sen okumak isteyebilirsiniz.. Java Garbage Collection Bazılarının nasıl çalıştığı hakkında daha iyi bir fikir edinmek için temel bilgiler .

Yani, yığına bir şey tahsis edilmişse, bunun bir parçası olarak toplanan çöp değildir System.gc()- yığın çerçevesi açıldığında serbest bırakılır. Yığında bir şey varsa ve yığında bir şeyden başvuruda bulunulursa, o zaman toplanan çöpler olmaz.

Bu neden önemli?

Çoğunlukla, geleneği. Yazılan kitap ve derleyici sınıfları ve çeşitli bit belgeleri, yığın ve yığında önemli bir yer tutar.

Bununla birlikte, günümüzün sanal makineleri (JVM ve benzeri), bunu programlayıcıdan gizli tutmaya çalışmak için oldukça uzadı. Birinden veya diğerinden bitmediğiniz ve nedenini bilmek zorunda olmadıkça (sadece depolama alanını uygun şekilde artırmak yerine) çok fazla önemli değil.

Nesne, bir yerde ve bulunduğu yerde uygun bir süre boyunca doğru ve hızlı bir şekilde erişilebildiği yerdedir. Yığın ya da yığın üzerinde ise - gerçekten önemli değil.


7
  1. Yığında, yığındaki bir işaretçi tarafından başvurulan nesnenin bir parçası olarak. yani. a ve b birbirine bitişik olarak depolanacaktır.
  2. Çünkü bütün hafıza yığın hafıza olsaydı, artık verimli olmazdı. Başlayacağımız küçük ve hızlı erişilen bir alana sahip olmak ve kalan referans alanlarına ait daha geniş bellek alanında referans almak mümkün. Bununla birlikte, bir nesne basitçe, işaretçi ile aynı yığın alanını kaplayacak tek bir ilkel olduğunda, bu çok üzülür.
  3. Evet.

1
2. noktanıza şunu ekleyeceğim: Yığında nesneler saklarsanız (yüz binlerce girişli bir sözlük nesnesi hayal ederseniz), nesneye iletmek veya her bir nesneyi kopyalamak için bir işlevden döndürmek için eklerim. saati. Bir işaretçi veya yığıntaki bir nesneye başvuru kullanarak, yalnızca (küçük) başvuruyu geçiyoruz.
Scott Whitlock

1
3'ün 'hayır' olacağını düşündüm, çünkü eğer nesne çöp toplanırsa, yığın içinde ona işaret eden bir referans yoktur.
Luciano

@ Luciano - Amacınızı anlıyorum. 3. soruyu farklı okudum. "Aynı zamanda" veya "o sırada" tam olarak açıktır. :: shrug ::
pdr

3
  1. Java, yığın örneğindeki yığına sınıf örneğini ayırmadığı sürece, kaçış analizi yaparak bunun anlamsallığı etkilemeyeceğini ispatladıktan sonra bir optimizasyon olarak tahsis etmedi. Bu bir uygulama ayrıntısı olsa da, mikro-optimizasyon hariç tüm pratik amaçlar için cevap “öbek” tir.

  2. Yığın hafızası ilk sırada olan sırayla tahsis edilmeli ve çıkarılmalıdır. Yığın hafızası herhangi bir sırayla tahsis edilebilir ve yerinden çıkarılabilir.

  3. Nesne çöp toplandığında, yığından işaret eden başka referans yoktur. Olsaydı, nesneyi canlı tutarlardı. Yığın ilkelleri hiçbir zaman çöp toplanmaz, çünkü işlev döndüğünde otomatik olarak yok edilirler.


3

Yığın belleği, yerel değişkenleri ve işlev çağrısını saklamak için kullanılır.

Öbek belleği Java'da nesneleri depolamak için kullanılırken. Olursa olsun, kod nerede nesne oluşturulur.

Burada ilkel olacaktır aiçinde class Asaklanabilir?

Bu durumda ilkel a, A sınıfı nesne ile ilişkilendirilir. Böylece Öbek Hafızasında yaratır.

Yığın belleği neden hiç yok? Neden her şeyi yığında saklayamıyoruz?

  • Yığında oluşturulan değişkenler kapsam dışına çıkar ve otomatik olarak yok edilir.
  • Yığın üzerindeki değişkenlerle karşılaştırıldığında yığın ayırmak çok daha hızlıdır.
  • Öbek üzerindeki değişkenler Çöp Toplayıcı tarafından imha edilmelidir.
  • Yığındaki değişkenlere kıyasla ayırmak için daha yavaş.
  • Derleme süresinden önce ne kadar veri ayırmanız gerektiğini tam olarak biliyorsanız ve çok büyük değilse yığını kullanırsınız (ilkel yerel değişkenler yığında depolanır).
  • Çalışma zamanında tam olarak ne kadar veriye ihtiyaç duyacağınızı bilmiyorsanız veya çok fazla veri ayırmanız gerekiyorsa, öbek kullanırsınız.

Nesne çöp toplandığında, nesne ile ilişkili yığın imha edilir mi?

Çöp Toplayıcı, Yığın belleği kapsamında çalışır, bu nedenle referans zinciri olmayan nesneleri kökten yok eder.


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.