Java'nın String sabit havuzu nerede yaşıyor, yığın mı yoksa yığın mı?


104

Bir sabitler havuzu ve JVM'ler tarafından String değişmezlerini işlemek için kullanılan String sabit havuzu kavramını biliyorum. Ancak, String sabiti değişmezlerini depolamak için JVM tarafından hangi tür bellek kullanıldığını bilmiyorum. Yığın mı yoksa yığın mı? Herhangi bir örnekle ilişkili olmayan bir literal olduğundan, yığın halinde depolanacağını varsayabilirim. Ancak, herhangi bir örnek tarafından atıfta bulunulmamışsa, hazır bilgi GC çalıştırması tarafından toplanmalıdır (yanılıyorsam düzeltin), öyleyse yığında saklanırsa bu nasıl ele alınır?


11
Yığın üzerinde bir havuz nasıl saklanabilir? yığın kavramını biliyor musun?
The Scrum Meister

1
Merhaba Scrum Meister, olamaz demek istedim. Yanlış kongre için özür dilerim. GC ile ilgili Şimdi öğrenmeye başladım. Bunun için teşekkürler
Rengasami Ramanujam

@TheScrumMeister - aslında, bazı koşullar altında onlar olabilir çöp toplandı. "Anlaşma kesicisi", bir dize değişmezinden bahseden herhangi bir sınıfın kod nesnesinin, değişmezi temsil eden String nesnesine bir başvurusu olacağıdır.
Stephen C

Yanıtlar:


74

Cevap teknik olarak ikisi de değil. Java Sanal Makine Belirtimine göre, dize değişmezlerini depolama alanı, çalışma zamanı sabit havuzundadır . Çalışma zamanı sabit havuz bellek alanı, sınıf başına veya arabirim bazında tahsis edilir, bu nedenle herhangi bir nesne örneğine bağlı değildir. Çalışma zamanı sabit havuzu, "çalışma zamanı sabit havuzu, alan ve yöntem verileri gibi sınıfa göre yapıları ve sınıf ve örnek başlatma ve arabirimde kullanılan özel yöntemler de dahil olmak üzere yöntemler ve yapıcılar için kodlar gibi sınıfa göre yapıları depolayan" yöntem alanının bir alt kümesidir. tür başlatma ". VM spesifikasyonu, yöntem alanının mantıksal olarak yığının bir parçasıdır, yöntem alanında ayrılan belleğin çöp toplamaya veya öbeğe tahsis edilen normal veri yapıları ile ilişkili diğer davranışlara tabi olmasını gerektirmez.


8
Aslında, sınıflar VM'de yüklendiğinde, dize sabitleri yığına, VM çapında bir dize havuzuna (Stephen C'nin dediği gibi permgen içinde) kopyalanacaktır, çünkü farklı sınıflardaki eşit dize değişmezleri aynı String nesnesi (JLS tarafından).
Paŭlo Ebermann

1
Cevaplarınız için hepinize teşekkür ederim. Bu tartışmadan çok şey anladım. Sizi tanımak güzel :)
Rengasami Ramanujam

4
Paŭlo, bu Sun'ın sanal makinesi için doğrudur, ancak JVM'nin tüm uygulamaları için zorunlu olarak doğru değildir. JVM spesifikasyonunda belirtildiği gibi, çalışma zamanı sabit havuzu ve yöntem alanı mantıksal olarak yığının parçası olsa da, aynı davranışa sahip olmaları gerekmez. Sadece küçük bir anlamsal fark, gerçekten :)
Duane Moore


54

Bu yanıtla açıklandığı gibi , dize havuzunun tam konumu belirtilmemiştir ve bir JVM uygulamasından diğerine değişebilir.

İlginçtir ki, Java 7'ye kadar havuz, hotspot JVM'deki yığının permgen uzayındaydı, ancak Java 7'den beri yığının ana kısmına taşındı :

Alan : HotSpot
Özet : JDK 7'de, dahili dizeler artık Java yığınının kalıcı neslinde tahsis edilmiyor , bunun yerine Java yığınının ana bölümünde (genç ve eski nesiller olarak bilinir) diğeriyle birlikte tahsis ediliyor uygulama tarafından oluşturulan nesneler. Bu değişiklik, ana Java yığınında daha fazla verinin bulunmasına ve kalıcı üretimde daha az veriye neden olacaktır ve bu nedenle yığın boyutlarının ayarlanmasını gerektirebilir. Çoğu uygulama, bu değişiklik nedeniyle yığın kullanımında yalnızca nispeten küçük farklılıklar görecektir, ancak birçok sınıf yükleyen veya String.intern () yöntemini yoğun bir şekilde kullanan daha büyük uygulamalar daha önemli farklılıklar görecektir. RFE: 6962931

Java 8 Hotspot'ta da Permanent Generation tamamen kaldırıldı.


30

Dize değişmezleri yığında depolanmaz. Asla. Aslında, yığın üzerinde hiçbir nesne depolanmaz.

(Veya daha doğru bir şekilde, onları temsil String nesneleri) dize hazır olan tarihi bir yığın içinde saklanmıştır "PermGen" yığın olarak adlandırılan. (Permgen, kalıcı üretimin kısaltmasıdır.)

Normal koşullar altında, String değişmezleri ve permgen yığınındaki diğer şeylerin çoğu "kalıcı olarak" erişilebilirdir ve çöp olarak toplanmaz. (Örneğin, String değişmez değerlerine, onları kullanan kod nesnelerinden her zaman erişilebilir.) Bununla birlikte, artık gerekli olmayan dinamik olarak yüklenmiş sınıfları bulmaya ve toplamaya çalışmak için bir JVM yapılandırabilirsiniz ve bu, String değişmez değerlerinin çöp olarak toplanmasına neden olabilir. .

AÇIKLAMA # 1 - Permgen'in GC'ye tabi tutulmadığını söylemiyorum. Genellikle JVM bir Tam GC çalıştırmaya karar verdiğinde yapar. Demek istediğim , String değişmez değerlerine , onları kullanan kod erişilebilir olduğu sürece erişilebilir olacak ve kodun sınıf yükleyicisi erişilebilir olduğu sürece koda erişilebilir olacak ve varsayılan sınıf yükleyiciler için bu "sonsuza kadar" anlamına geliyor.

AÇIKLAMA # 2 - Aslında, Java 7 ve sonrası, dizi havuzunu tutmak için normal öbek kullanır. Böylece, String değişmezlerini ve stajyer dizeleri temsil eden String nesneleri aslında normal yığın içindedir. (Ayrıntılar için @ assylias'ın Yanıtı'na bakın.)


Ama hala dize değişmezinin depolanması ile oluşturulan dize arasındaki ince çizgiyi bulmaya çalışıyorum new.

"İnce çizgi" yoktur. Gerçekten çok basit:

  • String Dize değişmezlerini temsil eden / bunlara karşılık gelen nesneler dize havuzunda tutulur.
  • Stringbir String::internçağrı tarafından oluşturulan nesneler dize havuzunda tutulur.
  • Diğer tüm Stringnesneler dizi havuzunda TUTULMAZ.

Sonra, string havuzunun nerede "depolandığı" ile ilgili ayrı bir soru var. Java 7'den önce permgen yığınıydı. Java 7'den itibaren ana yığındır.


23

Dize havuzu

Dize havuzu oluşturma (bazen dize standartlaştırma olarak da adlandırılır), birkaç String nesnesini eşit değerde ancak farklı kimliğe sahip tek bir paylaşılan String nesnesiyle değiştirme işlemidir. Bu hedefe kendi Haritanızı (gereksinimlerinize bağlı olarak muhtemelen yumuşak veya zayıf referanslarla) ve harita değerlerini kanonik değerler olarak kullanarak gerçekleştirebilirsiniz. Veya JDK tarafından size sağlanan String.intern () yöntemini kullanabilirsiniz.

Java 6'nın String.intern () kullanımı zaman zaman, havuzlama kontrolden çıktığında OutOfMemoryException alma olasılığının yüksek olması nedeniyle birçok standart tarafından yasaklanmıştı. Dizi havuzlamasının Oracle Java 7 uygulaması önemli ölçüde değiştirildi. Ayrıntıları http://bugs.sun.com/view_bug.do?bug_id=6962931 ve http://bugs.sun.com/view_bug.do?bug_id=6962930 adreslerinde arayabilirsiniz .

Java 6'da String.intern ()

O eski güzel günlerde tüm dizgiler, ağırlıklı olarak yüklü sınıfları ve dizi havuzunu depolamak için kullanılan yığının sabit boyutlu kısmı olan PermGen'de saklanıyordu. Açıkça dahili dizelerin yanı sıra, PermGen dize havuzu, programınızda daha önce kullanılan tüm değişmez dizeleri de içerir (buradaki önemli kelime kullanılır - eğer bir sınıf veya yöntem hiç yüklenmemiş / çağrılmamışsa, içinde tanımlanan sabitler yüklenmeyecektir).

Java 6'daki bu tür dizgi havuzuyla ilgili en büyük sorun konumuydu - PermGen. PermGen sabit bir boyuta sahiptir ve çalışma zamanında genişletilemez. -XX: MaxPermSize = 96m seçeneğini kullanarak ayarlayabilirsiniz. Bildiğim kadarıyla varsayılan PermGen boyutu platforma bağlı olarak 32M ile 96M arasında değişiyor. Boyutunu artırabilirsiniz, ancak boyutu yine de sabit olacaktır. Bu tür bir sınırlama, String.intern'in çok dikkatli kullanılmasını gerektirdi - bu yöntemi kullanarak herhangi bir kontrolsüz kullanıcı girdisini dahil etmemelisiniz. Bu nedenle, Java 6 zamanlarında dize havuzu çoğunlukla manuel olarak yönetilen haritalarda uygulanmıştır.

Java 7'de String.intern ()

Oracle mühendisleri, Java 7'deki dizi havuzlama mantığında son derece önemli bir değişiklik yaptı - dize havuzu yığına taşındı. Bu, artık ayrı bir sabit boyutlu bellek alanıyla sınırlı olmadığınız anlamına gelir. Diğer sıradan nesnelerin çoğu gibi tüm dizeler artık yığında yer alır ve bu da uygulamanızı ayarlarken yalnızca yığın boyutunu yönetmenize olanak tanır. Teknik olarak, Java 7 programlarınızda String.intern () kullanmayı yeniden düşünmek için bu tek başına yeterli bir neden olabilir. Ancak başka sebepler de var.

Dize havuzu değerleri çöp olarak toplanır

Evet, JVM dizgi havuzundaki tüm dizeler, program köklerinizden bunlara herhangi bir başvuru yoksa, çöp toplama için uygundur. Java'nın tartışılan tüm sürümleri için geçerlidir. Bu, stajyer diziniz kapsam dışına çıkarsa ve ona başka başvurular yoksa, JVM dize havuzundan çöp toplanacağı anlamına gelir.

Çöp toplama için uygun olan ve yığın içinde bulunan bir JVM dizgi havuzu, tüm dizeleriniz için doğru bir yer gibi görünüyor, değil mi? Teorik olarak bu doğrudur - kullanılmayan dizeler havuzdan toplanan çöpler olacaktır, kullanılan dizeler, girdiden eşit bir dizge almanız durumunda bellekten tasarruf etmenizi sağlayacaktır. Mükemmel bir hafıza tasarrufu stratejisi gibi görünüyor? Neredeyse öyle. Herhangi bir karar vermeden önce dize havuzunun nasıl uygulandığını bilmelisiniz.

kaynak.


11

Diğer cevapların açıkladığı gibi Java'da Bellek iki bölüme ayrılmıştır

1. Yığın: İş parçacığı başına bir yığın oluşturulur ve yerel değişkenleri yeniden depolayan yığın çerçevelerini depolar ve eğer bir değişken bir referans türüyse, o değişken gerçek nesne için yığın içindeki bir bellek konumuna başvurur.

2. Yığın: Her türlü nesne yalnızca yığın halinde oluşturulacaktır.

Yığın hafızası yine 3 kısma bölünmüştür

1. Young Generation: Kısa ömürlü nesneleri depolar, Young Generation kendisini Eden Space ve Survivor Space olarak iki kategoriye ayırabilir .

2. Eski Nesil: Pek çok çöp toplama döngüsünden sağ çıkmış ve hala başvurulan nesneleri depolayın.

3. Kalıcı Üretim: Programla ilgili meta verileri depolar, örneğin çalışma zamanı sabit havuzu.

Dize sabit havuzu, Heap belleğinin kalıcı üretim alanına aittir.

javap -verbose class_nameBize yöntem referanslarını (#Methodref), Sınıf nesnelerini (#Class), string değişmezlerini (#String) gösterecek olan bytecode'daki kodumuz için çalışma zamanı sabit havuzunu görebiliriz.

çalışma zamanı sabit havuzu

JVM Metodu Aşırı Yüklemeyi ve Dahili Olarak Geçersiz Kılmayı Nasıl İşliyor makalemde daha fazla bilgi edinebilirsiniz .


Lütfen herhangi bir bağlı kuruluşu açıklayın ve siteyi gönderi yoluyla sitenizi tanıtmanın bir yolu olarak kullanmayın. Bkz Ben iyi cevap yazmak nasıl? .

9

Burada zaten yer alan harika cevaplara, bakış açımda eksik olan bir şeyi eklemek istiyorum - bir örnek.

Siz zaten JVM, ayrılmış belleği bir Java programına iki bölüme ayırır. biri yığın , diğeri yığın . Stack yürütme amaçlı, yığın ise depolama amacıyla kullanılır. Bu yığın belleğinde, JVM, özellikle dizge değişmezleri için tasarlanmış bir miktar bellek ayırır. Yığın belleğin bu kısmına dizge sabitleri havuzu denir .

Örneğin, aşağıdaki nesneleri başlatırsanız:

String s1 = "abc"; 
String s2 = "123";
String obj1 = new String("abc");
String obj2 = new String("def");
String obj3 = new String("456);

Dize değişmezleri s1ve s2dizge sabit havuzuna, nesneler obj1, obj2, obj3 öbeğine gider. Hepsi, Yığından referans alınacaktır.

Ayrıca, "abc" nin yığın ve dizge sabit havuzunda görüneceğini lütfen unutmayın. Neden bu şekilde yaratılıyor String s1 = "abc"ve String obj1 = new String("abc")yaratılacak? Bunun nedeni , bir String nesnesinin String obj1 = new String("abc")açıkça yeni ve referans olarak farklı bir örneğini oluşturması ve String s1 = "abc"varsa dize sabit havuzundan bir örneği yeniden kullanabilmesidir. Daha ayrıntılı bir açıklama için: https://stackoverflow.com/a/3298542/2811258

görüntü açıklamasını buraya girin


Verilen diyagramda, "def" ve "456" değişmezleri nerede bulunurdu. Ve bunlara nasıl referans verilecek?
Satyendra

Yorumunuz için teşekkürler @Satyendra, resmi ve cevabı güncelledim.
Johnny

@Stas neden başka bir String nesnesi "abc" yaratıldı .. değişmez hakkı işaret etmek için obj1 referansını kullanmalı?

Bunun nedeni, String obj1 = new String ("abc"), bir String nesnesinin yeni ve referans olarak farklı bir örneğini açıkça oluşturur ve String s1 = "abc", varsa, dize sabit havuzundan bir örneği yeniden kullanabilir. Daha ayrıntılı bir açıklama için: stackoverflow.com/a/3298542/2811258
Johnny
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.