Dizeye "" eklemek neden bellek tasarrufu sağlıyor?


193

İçinde çok fazla veri bulunan bir değişken kullandım String data. Bu dizenin küçük bir bölümünü aşağıdaki şekilde kullanmak istedim:

this.smallpart = data.substring(12,18);

Birkaç saat hata ayıklamadan sonra (bir bellek görselleştiricisi ile) , yalnızca alt dizeyi içermesine rağmen , nesneler alanının smallparttüm verileri hatırladığını öğrendim data.

Kodu değiştirdiğimde:

this.smallpart = data.substring(12,18)+""; 

..Problem çözüldü! Şimdi benim uygulama şimdi çok az bellek kullanıyor!

Bu nasıl mümkün olabilir? Herkes bunu açıklayabilir mi? Bence bu.smallpart verilere atıfta bulundu, ama neden?

GÜNCELLEME: O zaman büyük String'i nasıl silebilirim? Data = new String (data.substring (0,100)) işi yapacak mı?


Aşağıda nihai amacınız hakkında daha fazla bilgi: Büyük dize ilk olarak nereden geliyor? Bir dosyadan veya veritabanından CLOB veya başka bir şeyden okunduysanız, yalnızca ayrıştırırken ihtiyaç duyduğunuz şeyleri okumak en uygun yol olacaktır.
PSpeed

4
İnanılmaz ... Java'da 4-5 yıldan fazla çalışıyorum, yine de bu benim için yeni :). bilgi için teşekkürler kardeşim.
Parth

1
Kullanmanın bir inceliği vardır new String(String); bkz. stackoverflow.com/a/390854/8946 .
Lawrence Dol

Yanıtlar:


159

Aşağıdakileri yapmak:

data.substring(x, y) + ""

yeni (daha küçük) bir String nesnesi oluşturur ve substring () tarafından oluşturulan String'e yapılan başvuruyu atar, böylece bunun çöp toplanması sağlanır.

Gerçekleştirilmesi gereken önemli şey, mevcut bir String'e (ya da daha doğrusu orijinal String'in altında yatan karakter dizisine) substring()bir pencere vermesidir . Bu nedenle orijinal Dize ile aynı belleği tüketecektir. Bu, bazı durumlarda avantajlı olabilir, ancak bir alt dize almak ve orijinal Dizeyi atmak istiyorsanız (bildiğiniz gibi) sorunludur.

Bir göz atın alt dize () yönteminin daha fazla bilgi için JDK dize kaynağı.

DÜZENLEME: Ek sorunuzu yanıtlamak için, alt dizeden yeni bir Dize oluşturmak, orijinal Dize'ye herhangi bir başvuruda bulunmanız koşuluyla bellek tüketiminizi azaltır .

NOT (Ocak 2013). Yukarıdaki davranış Java 7u6'da değişmiştir . Sinek siklet modeli artık kullanılmamaktadır ve substring()beklediğiniz gibi çalışacaktır.


89
Bu, String(String)kurucunun (yani bir String'i girdi olarak alan String kurucusunun) yararlı olduğu çok az durumdan biridir : new String(data.substring(x, y))ekleme ile aynı şeyi etkili bir şekilde ""yapar, ancak amacı biraz daha açık hale getirir.
Joachim Sauer

3
sadece dize value, orijinal dize niteliğini kullanır . Sanırım referans bu yüzden korunuyor.
Valentin Rocher

@Bishiboosh - evet, doğru. Uygulamanın özelliklerini ortaya çıkarmak istemedim, ama tam olarak olan bu.
Brian Agnew

5
Teknik olarak bu bir uygulama detayıdır. Ancak yine de sinir bozucu ve birçok insanı yakalar.
Brian Agnew

1
Bunu zayıf referanslar veya benzeri kullanarak JDK'da optimize etmenin mümkün olup olmadığını merak ediyorum. Bu karaktere [] ihtiyaç duyan son kişiysem ve sadece birazına ihtiyacım varsa, dahili olarak kullanmam için yeni bir dizi yap.
WW.

28

Kaynağına bakarsanız, substring(int, int)geri döndüğünü görürsünüz:

new String(offset + beginIndex, endIndex - beginIndex, value);

valueorijinal nerede char[]. Böylece aynı dizeye sahip yeni bir String elde edersiniz char[].

Bunu yaptığınızda, data.substring() + "", bir ile yeni bir dize olsun yeni yatan char[].

Aslında, kullanım durumunuz yapıcıyı kullanmanız gereken tek durumdur String(String):

String tiny = new String(huge.substring(12,18));

1
Kullanmanın bir inceliği vardır new String(String); bkz. stackoverflow.com/a/390854/8946 .
Lawrence Dol

17

Kullandığınızda substring, aslında yeni bir dize oluşturmaz. Hala ofset ve boyut kısıtlamasıyla orijinal dizenizi ifade eder.

Bu nedenle, orijinal dizenizin toplanmasına izin vermek için yeni bir dize oluşturmanız gerekir (kullanarak new Stringveya sahip olduklarınızı ).


5

Bence bu.smallpart verilere atıfta bulundu, ama neden?

Java dizeleri bir char dizisinden, bir başlangıç ​​ofsetinden ve bir uzunluktan (ve bir önbellek hashCode) oluşur. substring()Orijinalin char dizisini paylaşan ve farklı ofset ve / veya uzunluk alanlarına sahip yeni bir String nesnesi oluşturmak gibi bazı String işlemleri . Bu, bir String'in char dizisi oluşturulduktan sonra asla değiştirilmediği için çalışır.

Bu, birçok alt dize çakışan parçaları çoğaltmadan aynı temel dizeye başvurduğunda bellek tasarrufu sağlayabilir. Fark ettiğiniz gibi, bazı durumlarda, artık gerekli olmayan verileri artık çöp toplamaktan alıkoyabilir.

Bunu düzeltmenin "doğru" yolu new String(String)yapıcıdır, yani

this.smallpart = new String(data.substring(12,18));

BTW, genel olarak en iyi çözüm, ilk etapta çok büyük Dizeler kullanmaktan kaçınmak ve her seferinde bir kaç KB olmak üzere daha küçük parçalar halinde herhangi bir girdiyi işlemek olacaktır.


Kullanmanın bir inceliği vardır new String(String); bkz. stackoverflow.com/a/390854/8946 .
Lawrence Dol

5

Java dizelerinde değişmez nesneler vardır ve bir dize oluşturulduktan sonra, çöp toplayıcı tarafından temizlenene kadar bellekte kalır (ve bu temizlik verilebilecek bir şey değildir).

Alt dize yöntemini çağırdığınızda, Java tamamen yeni bir dize oluşturmaz, ancak yalnızca orijinal dizenin içinde bir dizi karakter saklar.

Bu kodla yeni bir dize oluşturduğunuzda:

this.smallpart = data.substring(12, 18) + ""; 

sonucu boş dize ile birleştirdiğinizde yeni bir dize oluşturdunuz. Bu yüzden.


3

1997 yılında jwz tarafından belgelendiği gibi :

Büyük bir dizeniz varsa, bir alt dizesini () çıkarın, alt dizeye tutun ve daha uzun dizenin çöp haline gelmesine izin verin (başka bir deyişle, alt dizinin ömrü daha uzun olur), büyük dizenin temel baytları asla gitmez uzakta.


2

Özetlemek gerekirse, az sayıda büyük dizeden çok sayıda alt dize oluşturursanız,

   String subtring = string.substring(5,23)

Alanı yalnızca büyük dizeleri saklamak için kullandığınız için, ancak sadece bir avuç küçük dizeyi, büyük dizelerin kaybından çıkarıyorsanız,

   String substring = new String(string.substring(5,23));

Büyük dizeler artık gerekmediğinde geri alınabileceğinden bellek kullanımınızı düşük tutacaktır.

Aradığınızı new String, orijinal olana başvurmak yerine gerçekten yeni bir dize aldığınız yararlı bir hatırlatmadır.


Kullanmanın bir inceliği vardır new String(String); bkz. stackoverflow.com/a/390854/8946 .
Lawrence Dol

2

İlk olarak, çağrı java.lang.String.substringString, altta yatan dizinin önemli kısmını kopyalamak yerine , ofset ve uzunluk kullanılarak orijinal üzerinde yeni bir pencere oluşturur .

substringYönteme daha yakından bakacak olursak, bir string yapıcı çağrısını String(int, int, char[])ve dizeyichar[] temsil eden bütününü geçirdiğimizi göreceğiz . Bu, alt dizenin orijinal dize kadar bellek kaplayacağı anlamına gelir .

Tamam, ama neden + ""onsuz daha az bellek için talep sonuçlar ??

Bir yaparak +AÇIK stringsüzerinden uygulanmaktadır StringBuilder.appendyöntem çağrısı. AbstractStringBuilderSınıfta bu yöntemin uygulanmasına bakın, nihayetinde arraycopygerçekten ihtiyaç duyduğumuz kısım ile yapıldığını söyleyecektir substring.

Başka bir geçici çözüm ??

this.smallpart = new String(data.substring(12,18));
this.smallpart = data.substring(12,18).intern();

0

Bir dizeye "" eklenmesi bazen bellek tasarrufu sağlar.

Diyelim ki bir kitabın tamamını içeren bir dizim var, bir milyon karakter.

Sonra kitabın alt dizeleri olarak bölümlerini içeren 20 dize oluşturuyorum.

Sonra tüm paragrafları içeren 1000 dize oluşturun.

Sonra tüm cümleleri içeren 10.000 karakter dizisi oluşturuyorum.

Sonra tüm kelimeleri içeren 100.000 karakter dizisi oluşturuyorum.

Hala sadece 1.000.000 karakter kullanıyorum. Her bölüm, paragraf, cümle ve kelimeye "" eklerseniz, 5.000.000 karakter kullanırsınız.

Tabii ki tüm kitaptan tek bir kelime çıkarırsanız tamamen farklıdır ve tüm kitap çöp toplanmış olabilir, ancak tek bir kelimenin referansı olduğu için değil.

Ve bir milyon karakter dizeniz varsa ve her iki uçtaki sekmeleri ve boşlukları kaldırarak, bir alt dize oluşturmak için 10 çağrı yaparak tekrar farklıdır. Java'nın çalışma veya çalışma şekli her seferinde bir milyon karakter kopyalamaktan kaçınır. Uzlaşma var ve uzlaşmaların ne olduğunu biliyorsanız iyi olur.

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.