Java’da nesne yaratmadan kaçınmalı mıyız?


243

Bir meslektaşım tarafından Java nesnesi yaratmada gerçekleştirebileceğiniz en pahalı işlem olduğu söylendi. Bu yüzden sadece mümkün olduğu kadar az nesne oluşturmaya karar verdim.

Bu, nesneye yönelik programlamanın amacını yenmek için biraz görünüyor. Nesneler yaratmıyorsak, optimizasyon için sadece bir tane uzun C sınıfı yazıyoruz.


39
"Java nesnesi oluşturma işlemi gerçekleştirebileceğiniz en pahalı işlemdir" . Bu iddianın kaynağını paylaşmak ister misiniz?
Songo

92
Ve - neye göre en pahalı operasyon? Pi'yi 900 haneye kadar hesaplamakla mı yoksa int değerini artırmakla mı karşılaştırmak?
jasonk

4
Bu özel nesne yaratımının belirli bir bölümünden bahsediyor olabilirler mi? Apple'ın tableViewCells için nasıl bir sıra kullandığını düşünüyorum. Belki meslektaşınız birkaç nesne yaratmayı ve bu belirli nesnelerle ilişkili bazı ek yükler nedeniyle onları yeniden kullanmayı öneriyordu? Sadece neden böyle bir iddiada bulunduklarını anlamaya çalışıyorum.
Tyler DeWitt

3
Programlama hakkında duyduğum en komik şeylerden biri.)
Mert Akcakaya

22
Aritmatik de oldukça pahalıdır; hesaplamalardan kaçınmalıyız, oh, ve JVM'yi başlatmak gerçekten pahalıdır, bu yüzden JVM'leri başlatmamalıyız :-).
James Anderson,

Yanıtlar:


478

Meslektaşınızın ne hakkında konuştukları hakkında hiçbir fikri yok.

En pahalı operasyonunuz onları dinliyor olacaktı . Sizi, on yıldan daha eski olan bilgilere (bu cevabın asıl tarihi itibariyle) yanı sıra burada göndermek ve İnternet'i gerçeği araştırmak için zaman harcamak zorunda bırakarak sizi yanlış yönlendirerek zamanınızı boşa harcadılar .

Umarım onlar sadece on yıl önce duydukları veya okudukları bir şeyi cahilce yetersiz tutarlar ve daha iyisini bilmiyorlar. Ben de şüpheli olarak söylediklerini başka bir şey alırdım, bu her iki şekilde de güncel olan herkes tarafından iyi bilinen bir yanlışlık olmalı.

Herşey bir Nesnedir (hariç primitives)

İlkellerden ( int, long, double, vb.) Başka her şey Java'daki Nesnelerdir. Java'da Nesne oluşturulmasını önlemenin bir yolu yoktur.

Java'da bellek ayırma stratejileri nedeniyle nesne oluşturma çoğu durumda C ++ 'dan daha hızlıdır ve JVM'deki diğer her şeye kıyasla tüm pratik amaçlar için "özgür" kabul edilebilir .

1990'ların sonlarında olduğu gibi, 2000'lerin başlarında JVM uygulamaları, Object'lerin fiili tahsisinde genel bir performans gösterdi. En azından 2005'ten beri durum böyle değildi.

Eğer melodi Eğer -Xmsdoğru çalıştırmak için uygulama için gereken tüm hafızayı desteklemek için, GC çalıştırmak ve kısa ömürlü programlar may asla GC hiç modern GC uygulamalarda çöp en süpürmek zorunda asla.

Yine de kırmızı bir ringa balığı olan boş alanı denemeyi ve en üst düzeye çıkarmaz, çalışma zamanının performansını en üst düzeye çıkarır. Bu JVM Yığınının neredeyse her zaman% 100 tahsis edildiği anlamına gelirse, öyle olsun. Ücretsiz JVM yığın hafızası size zaten orada oturan hiçbir şey vermiyor.

GC'nin sistemin geri kalan kısmına yararlı bir şekilde belleği boşaltacağı yanılgısı vardır, bu tamamen yanlıştır!

JVM yığını büyüyüp daralmaz, böylece sistemin geri kalanı JVM Yığındaki boş hafızadan olumlu bir şekilde etkilenir . -XmsBaşlangıçta belirtilenlerin ALL'sini tahsis eder ve sezgisel olanı, JVM'nin bu örneği tamamen sona erene kadar herhangi bir işletim sistemi işlemiyle paylaşılmak üzere bu belleğin hiçbir zaman işletim sistemine geri bırakılmamasıdır. -Xms=1GB -Xmx=1GBherhangi bir zamanda gerçekte ne kadar nesne oluşturulduğuna bakılmaksızın 1GB RAM tahsis eder. Yığın belleğinin yüzdelerinin serbest bırakılmasını sağlayan bazı ayarlar vardır, ancak tüm pratik amaçlar için JVM , bunun gerçekleşmesi için hiçbir zaman bu hafızanın yeteri kadarını serbest bırakamaz.bu yüzden başka hiçbir işlem bu belleği geri alamaz, bu nedenle sistemin geri kalanı JVM Yığınından da özgür olmamaktan faydalanamaz. Bunun için bir RFE 29-NOV-2006’da “kabul edildi” , ancak bu konuda hiçbir şey yapılmadı. Bu davranış otorite kimsenin endişesi olarak kabul edilmez.

Çok sayıda kısa, kısa ömürlü nesnenin yaratılmasının JVM'nin uzun süre duraklatmasına neden olduğu yanılgısı vardır, bu şimdi de yanlıştır.

Mevcut GC algoritmaları kısa ömürlü birçok küçük nesne oluşturmak için optimize edilmiştir , yani her programdaki Java nesneleri için temelde% 99 sezgiseldir. Yapılan girişimler Nesne havuzu oluşturma aslında JVM çoğu durumda kötü performans yapacaktır.

Bugün havuza ihtiyaç duyan tek Nesneler, JVM'ye harici sonlu kaynakları ifade eden Nesneler ; Soketler, Dosyalar, Veritabanı Bağlantıları vb. Yeniden kullanılabilir. Normal nesneler, bellek konumlarına doğrudan erişmenize izin veren dillerle aynı şekilde birleştirilemez . Nesne önbelleğe alma farklı bir kavramdır ve bazı insanların doğal olarak havuzlama dedikleri şey olabilir veya olmayabilir , iki kavram aynı değildir ve karıştırılmamalıdır.

Modern GC algoritmaları bu soruna sahip değil çünkü belirli bir nesilde boş hafıza gerektiğinde bir programa tahsis edilmiyorlar, tahsil ediyorlar. Yığın yeterince büyükse, duraklamalara neden olacak kadar uzun zamandır hiçbir ayrılma gerçekleşmez.

Nesneye Yönelik Dinamik diller, artık hesaplamaya duyarlı testlerde bile günümüzde C atıyor.


274
+1: "En pahalı operasyonunuz onları dinliyor olacak ...". Bir süredir duyduğum en iyisi.
rjnilsson

21
@DeadMG, kümülatif GC yükü olsa bile, Java C ++ 'dan daha hızlı olabilir (örneğin, yığın sıkıştırması nedeniyle, belirli veri yapıları için önbellek kayıplarını en aza indirir).
SK-mantık

13
@ SK-mantık: GC deterministik olmadığı için bunu kanıtlamak en iyisidir. Önbellek özlüyor asgariye indirmek için gelince, her nesne başka bir dolaylı olması gerektiğinde, önbellek alanı boşa ve yürütme süresini artırmak için yapmak zordur. Bununla birlikte, basitçe C ++ 'ta nesne havuzu veya bellek alanı gibi uygun bir ayırıcı kullanarak, çöp toplayıcısının performansını kolayca eşleştirebilir veya yenebilirsiniz. Örneğin _alloca, amorti edilenden daha hızlı performans gösterecek bir bellek arena sınıfı yazabilirsiniz .
DeadMG

33
Nesne oluşturma artık eskisinden daha ucuz. Yine de ücretsiz değil. Sana yalan söylediğini söyleyen var mı? Ve OO dilleri hakkında C'yi yenen bağlantı, gerçekten öğrenmeye çalışan birine abartılı bir cevaptır.
jasonk

17
Bu boktan kodla sonuçlandığımız bu tür cevaplar yüzünden. Doğru cevap, Java'da bir nesne oluşturmaktır, hem Java nesnesini yaratır hem de onu başlatır. İlk kısım ucuz, ikinci edebilirsiniz çok pahalı. İnsanlar, newanahtar kelimeyi bir sıcak nokta alanında kullanmadan önce, her zaman yapıcılarda neler olduğuna bakmalıdır . İnsanların Swing nesnelerinin yönteminde kullandıklarını gördüm new ImageIcon(Image). Bu paint()oldukça pahalı ve kullanıcı arayüzünün tamamını halsizleştiriyordu. Yani siyah beyaz bir cevap değil, bir newyerde kullanmadan önce düşünün .
qwertzguy

94

Alt satır: Nesneleri oluşturmak için kısayollar almak için tasarımınızdan ödün vermeyin. Gereksiz yere nesne oluşturmaktan kaçının. Mantıklıysa, gereksiz işlemlerden kaçınmak için tasarım yapın (her türden).

Çoğu cevabın aksine - evet, nesne tahsisinin DOES ile ilgili bir maliyeti vardır. Düşük bir maliyettir, ancak gereksiz nesneler oluşturmaktan kaçınmalısınız. Kodunuzda gereksiz hiçbir şeyden kaçınmanız gerektiği gibi. Büyük nesne grafikleri GC'yi yavaşlatır, muhtemelen daha fazla yöntem çağrısı yapacağınız, daha fazla CPU önbellek özeti tetikleyemediğiniz ve işleminizin düşük RAM durumlarında diske aktarılma olasılığını artıracağınız için daha uzun yürütme süreleri anlamına gelir.

Birisi bunun son derece önemli bir olay olduğunu söyleyemeden önce - Optimize etmeden önce ~ 50 satır veri işlemek için 20 + MB nesne yaratan uygulamaları profillendirdim. Dakikada yüze kadar istek gönderene kadar ve aniden dakikada 2GB veri oluşturduğunuzda, bu testte gayet iyi. 20 req / sn yapmak istiyorsanız, 400 MB nesneler yaratıyor ve sonra onu atıyorsunuz. 20 req / sn, iyi bir sunucu için küçük.


7
Gerçekten kod netlik açısından hiç fark etmez Bazı durumlarda ancak performansta az ya da çok büyük bir fark yaratabilir: Ben bir örnek ekleyebilir akımlardan okurken, örneğin, bir şeyler kullanmak nadir değildir gibi while(something) { byte[] buffer = new byte[10240]; ... readIntoBuffer(buffer); ...hangi may Örneğe kıyasla savurgan olmak byte[] buffer = new byte[10240]; while(something) { ... readIntoBuffer(buffer); ....
JimmyB

4
+1: Gereksiz nesne oluşturma (veya kaldırıldıktan sonra temizleme) kesinlikle bazen maliyetli olabilir. Java'daki 3D Grafikler / OpenGL kodu, GC'nin kare hızınıza zarar verebileceği için oluşturulan nesne sayısını en aza indirmek için yapılan optimizasyonları yaptığım bir yer.
Leo,

1
Vaov! 50'den fazla veri işlemek için 20+ MB? Kulağa çılgınca geliyor. Her durumda, bu nesneler uzun ömürlü mü? Çünkü GC için önemli olan durum budur. Öte yandan, yalnızca bellek gereksinimlerini tartışıyorsanız , bunun çöp toplama veya nesne oluşturma verimliliği ile ilgisi yoktur ...
Andres F.

1
Nesneler kısa ömürlüdür (genel durumda 0,5 saniyeden az). Bu çöp hacmi hala performansı etkiler.
jasonk

2
Gerçekte gerçek durmakta kararlı olduğun için teşekkür ederim, düşüncesiz bir mimarinin etkisiyle yaşayan herkes çok fazla ilişki kurabilir.
JM Becker

60

Aslında, Java dilinin (veya diğer yönetilen herhangi bir dilin) ​​mümkün kıldığı bellek yönetimi stratejileri nedeniyle, nesne oluşturma, genç kuşak adı verilen bir bellek bloğundaki bir göstergeyi arttırmaktan biraz daha fazlasıdır. Boş hafıza araması gereken C'den çok daha hızlı.

Maliyetin diğer kısmı nesne imhasıdır, ancak C ile karşılaştırmak zordur. Bir koleksiyonun maliyeti uzun vadede kaydedilen nesnelerin miktarına dayanmaktadır, ancak koleksiyonların sıklığı yaratılan nesnelerin miktarına dayanmaktadır ... Sonunda, hala C tarzı bellek yönetiminden çok daha hızlı.


7
+1 Çöp toplayıcı "sadece işe yarıyor" olsa da, her Java programcısı kuşak çöp toplayıcıyı öğrenmelidir.
benzado

18
Java'nın daha yeni sürümleri kaçış analizi yapabilir, bu da yığın üzerinde bir yöntemden kaçmayan nesneler için bellek ayırabileceği anlamına gelir; , yöntemin yığın çerçevesi çözülmeden otomatik olarak atılır (yöntem geri döndüğünde).
Jesper

4
Bir sonraki adım, kaçış analizi için (örneğin, bir saf kayıt küçük nesnenin hafıza "ayır" için Pointbir amacı 2 genel amaçlı yazmaçlarda uygun olabilir).
Joachim Sauer

6
@ Joachim Sauer: Bu, HotSpot VM'nin yeni uygulamalarında yapılan bir şey. Buna skaler replasman denir.
someguy

1
@someguy: Bir süre önce bir sonraki şey olarak okudum, ancak daha önce yapılıp yapılmadığını kontrol etmedim. Buna zaten sahip olduğumuzu duymak harika bir haber.
Joachim Sauer

38

Diğer posterler Java'da nesne oluşturmanın oldukça hızlı olduğunu ve normal Java uygulamalarında genellikle endişelenmemelisiniz.

Çok özel durumlarda bir çift vardır olduğunu nesne oluşturmayı önlemek için iyi bir fikir.

  • Gecikme duyarlı bir uygulama yazarken ve GC duraklamalarını önlemek istediğinizde. Ne kadar çok nesne üretirseniz, GC o kadar çok olur ve duraklama şansı o kadar artar. Bu, oyunlar, bazı medya uygulamaları, robotik kontrol, yüksek frekanslı ticaret vb. İle ilgili geçerli bir değerlendirme olabilir. Çözüm, önünüzdeki tüm nesneleri / dizileri önceden tahsis etmek ve yeniden kullanmaktır. Javolution , bu tür yetenek sağlamada uzmanlaşmış kütüphaneler var . Fakat tartışmasız, gecikmeyi gerçekten önemsiyorsanız, Java veya C # :-) yerine C / C ++ / assembler kullanıyor olmalısınız.
  • Kutulu ilkellerden (Çift, Tam sayı vb.) Kaçınmak , bazı durumlarda çok yararlı bir mikro optimizasyon olabilir. Kutusuz ilkel (çift, int vb.) Nesne başına düşen ek yükten kaçındığından, sayısal işleme vb. Gibi daha yoğun CPU yoğun işlerdir. Genellikle, ilkel diziler Java'da çok iyi performans gösterir, bu nedenle bunları sayı çatırmak için kullanmak istersiniz başka türlü nesneler.
  • In kısıtlı bellek durumlarında her nesneler küçük yükü (JVM uygulamaya bağlı tipik 8-16 bayt) taşır beri nesnesi oluşturulur (aktif) sayısını en aza indirmek istiyoruz. Bu gibi durumlarda, verilerinizi çok sayıda küçük nesne yerine saklamak için az sayıda büyük nesneyi veya diziyi tercih etmelisiniz.

3
Kaçış analizinin kısa ömürlü (sınırlı yaşam) nesnelerin istifte depolanmasına izin vereceğini kaydetmeye değer, bu nesneler ücretsiz olarak dağıtılabilir. İbm.com/ developerworks/java/library/j
jtp09275/

1
@ Richard: harika nokta kaçış analizi bazı çok iyi optimizasyonlar sağlar. Buna rağmen, güvendiğinize dikkat etmelisiniz: tüm Java uygulamalarında ve her koşulda olması garanti edilmez. Bu yüzden genellikle emin olmak için kıyaslama yapmanız gerekir.
mikera

2
Ayrıca,% 100 doğru kaçış analizi durma problemine eşdeğerdir. Karmaşık durumlarda kaçış analizine güvenmeyin, çünkü en azından bir süre yanlış cevap verebilir.
Jules

İlk ve son noktalar, hızlı bir şekilde serbest bırakılan küçük nesneler için geçerli değildir, onlar cenazesinde ölür (C'de yığın tahsisini düşünün, hemen hemen ücretsizdir) ve asla GC zamanlarını veya bellek tahsisini etkilemez. Java’daki çoğu nesne tahsisi bu kategoriye girer ve bu nedenle esasen ücretsizdir. İkinci nokta hala geçerlidir.
Bill K,

17

Meslektaşınızın söylediklerinde bir çekirdek çekirdeği var. Saygılarımla, nesne yaratma ile ilgili sorunun aslında çöp toplama olduğunu düşünüyorum . C ++ 'da programlayıcı hafızanın nasıl tahsis edileceğini tam olarak kontrol edebilir. Program, istediği kadar kısa veya çok miktarda toplanabilir. Ayrıca, C ++ programı onu oluşturandan farklı bir iş parçacığı kullanarak kabuğunu atabilir. Bu nedenle şu anda çalışan ipliğin temizlenmesi için hiçbir zaman durması gerekmez.

Buna karşılık, Java Virtual Machine (JVM) kullanılmayan hafızayı geri almak için kodunuzu düzenli aralıklarla durdurur. Çoğu Java geliştiricisi bu duraklamayı asla farketmez, çünkü genellikle seyrek ve çok kısadır. Topladığınız su miktarı veya JVM'niz ne kadar kısıtlıysa, bu duraklamalar o kadar sık ​​olur. Bu işlemi görselleştirmek için VisualVM gibi araçlar kullanabilirsiniz .

Java'nın son sürümlerinde, çöp toplama (GC) algoritması ayarlanabilir . Genel bir kural olarak, duraklamaları ne kadar kısaltmak isterseniz, sanal makinedeki ek yük o kadar pahalı olur (yani, GC işlemini koordine etmek için kullanılan CPU ve bellek).

Bu ne zaman önemli olabilir? Tutarlı bir milisaniyelik cevap oranını ne zaman önemsiyorsanız, GC ile ilgileneceksiniz. Java'da yazılmış otomatik işlem sistemleri duraklamaları en aza indirmek için JVM'yi yoğun şekilde ayarlar. Aksi takdirde Java yazacak olan firmalar, sistemlerin her zaman çok duyarlı olmaları gereken durumlarda C ++ 'a dönüşür.

Kayıt için, ben do not genelde nesne kaçınma affetmek! Nesne yönelimli programlama için varsayılan değerdir. Bu yaklaşımı yalnızca GC yolunuza çıkarsa ve ardından JVM'yi daha az süre duraklatmak üzere ayarlamaya çalıştıktan sonra ayarlayın. Java performans ayarlaması hakkında iyi bir kitap, Charlie Hunt ve Binu John'un Java Performansı .


10
Çoğu modern JVM, "dünyayı durdur" dan daha iyi çöp toplama algoritmalarını destekler. Çöp toplama işleminde harcanan itfa süresi, açıkça malloc / ücretsiz olarak adlandırılan harcadıklarından daha azdır. Ayrıca C ++ bellek yönetimi ayrı bir dizide çalışıyor mu?

10
Oracle'ın daha yeni Java sürümleri kaçış analizi yapabilir; bu, bir yöntemden kaçmayan nesnelerin yığında tahsis edildiğini ve bu sayede temizliği ücretsiz yapan - yöntem geri döndüğünde otomatik olarak kaldırıldığı anlamına gelir.
Jesper

2
@ ThorbjørnRavnAndersen: Gerçekten mi? Gerçekten pause- mı demek istiyorsun az GC veya "genellikle paralel-ama-bazen-a-küçük-bit duraklatma" GC demek? GC'nin bir programı nasıl durduramayacağını anlamıyorum, ancak aynı anda nesneleri hareket ettirebiliyorum ... tam anlamıyla benim için imkansız görünüyor ...
Mehrdad

2
@ ThorbjørnRavnAndersen: Nasıl "dünyayı durdurabilir" ama asla "JVM'yi duraklatmaz"? Bu hiç mantıklı değil ...
Mehrdad

2
@ ThorbjørnRavnAndersen: Hayır Gerçekten bilmiyorum; Sadece ne dediğini anlamıyorum. GC bazen programı duraklatır, bu durumda - "tanımsız" değil "duraksız" değildir, ya da programı asla duraklatmaz (dolayısıyla "duraksız" dır), bu durumda bunun nasıl olduğunu anlamadım "sürdürdüğü zaman dünyayı durdurur" ifadesine tekabül eder, çünkü bu, GC çalışırken programın duraklatıldığı anlamına gelir. Durumun hangisi (duraksız mı yoksa değil mi) ve bunun nasıl mümkün olduğunu açıklar mısınız?
Mehrdad


9

GC birçok kısa ömürlü nesne için ayarlanmıştır

Eğer nesne tahsisini önemsiz bir şekilde azaltabilirseniz,

Bir örnek, bir döngü içinde bir dize inşa ediyor olacak, naif bir şekilde

String str = "";
while(someCondition){
    //...
    str+= appendingString;
}

bu Stringher +=işlemde yeni bir nesne yaratır (artı a StringBuilderve yeni temel karakter dizisi)

Bunu kolayca yeniden yazabilirsiniz:

StringBuilder strB = new StringBuilder();
while(someCondition){
    //...
    strB.append(appendingString);
}
String str = strB.toString();

Bu desen (değişmez sonuç ve yerel değişken bir ara değer) başka şeylere de uygulanabilir.

ama bunun dışında hayaletleri kovalamak yerine gerçek tıkanıklığı bulmak için bir profilci bulmalısın


Bu en büyük avantajı StringBuilderyaklaşımı olan ön-boyStringBuilder yüzden zorundadır asla yeniden tahsis ile altta yatan dizi StringBuilder(int)yapıcısı. Bu, bir tahsisat yerine tek bir 1+Ntahsisat yapar.

2
@JarrodRoberson StringBuilder mevcut kapasiteyi en az iki katına çıkaracak veya bir başka deyişle, kapasite sadece log (n) tahsisleri için katlanarak artacaktır
cırcır böceği

6

Joshua Bloch (Java platformu oluşturucularından biri) 2001 yılında Effective Java adlı kitabında yazdı :

Kendi nesne havuzunuzu koruyarak nesne oluşturmayı önlemek, havuzdaki nesneler çok ağır olmadıkça kötü bir fikirdir. Bir nesne havuzunu haklı çıkartan bir nesnenin prototipik örneği bir veritabanı bağlantısıdır. Bağlantının kurulmasının maliyeti, bu nesnelerin yeniden kullanılmasının anlamlı olacağı kadar yüksektir. Bununla birlikte, genel olarak konuşursak, kendi nesne havuzlarınızı sürdürmek kodunuzu zorlar, bellek ayak izini artırır ve performansı bozar. Modern JVM uygulamaları, hafif nesneler üzerinde bu tür nesne havuzlarından daha iyi performans gösteren yüksek oranda optimize edilmiş çöp toplayıcılara sahiptir.


1
Ağ bağlantısı gibi şeylerde bile, önemli olan, bağlantılarla ilişkili GC ile ayrılmış nesneleri korumak değil, GC tarafından yönetilen dünyanın dışında var olan bağlantı kümesini korumaktır. Nesneleri havuzlamanın yararlı olabileceği zamanlar vardır; Bunların çoğu, aynı nesneye yapılan bir çift referansın, aynı fakat farklı nesnelere yapılan bir çift referansa anlamsal olarak denk olabileceği, ancak yine de bazı avantajlara sahip olduğu durumlardan kaynaklanmaktadır. Örneğin, aynı elli binlik karakter dizisine iki referans eşitlik açısından kontrol edilebilir ...
supercat

2
... bu uzunluktaki iki farklı fakat özdeş karakter dizisine referanstan çok daha hızlı.
supercat,

5

Bu gerçekten belirli bir uygulamaya bağlı, bu yüzden genel olarak söylemek gerçekten zor. Ancak, bir uygulamada nesne oluşturma işlemi aslında bir performans darboğazı olsaydı çok şaşırırdım. Yavaş olsalar bile, kod stili yararı muhtemelen performanstan ağır basacaktır (aslında kullanıcıya farkedilmezse)

Her durumda, tahmin yerine gerçek performans darboğazlarını belirlemek için kodunuzu belirleyene kadar bu konular hakkında endişelenmeye başlamamalısınız. O zamana kadar kod okunabilirliği için en iyi olanı performanstan ziyade yapmalısınız.


Java'nın ilk sürümlerinde, çöp toplayıcı atılan nesneleri temizlerken ortaya çıkan önemli bir maliyet vardı. Java'nın son sürümleri büyük ölçüde GC seçeneklerine sahipti, bu nedenle nesne oluşturma nadiren sorun oluyor. Genellikle web sistemleri, uygulama sunucusunda sayfa yükünün bir parçası olarak gerçekten karmaşık bir şey hesaplamadığınız sürece, dış sistemlerdeki çağrılarla sınırlıdır.
greg

3

Bence meslektaşın gereksiz nesne yaratma perspektifinden söylemiş olmalı. Aynı nesneyi sık sık oluşturuyorsanız, o nesneyi paylaşmak daha iyidir. Nesne oluşturma işleminin karmaşık olduğu ve daha fazla bellek harcadığı durumlarda bile, o nesneyi klonlamak ve bu karmaşık nesne oluşturma işlemini oluşturmaktan kaçınmak isteyebilirsiniz (Bu, gereksiniminize bağlıdır). "Nesne oluşturma pahalı" ifadesi bağlamında alınması gerektiğini düşünüyorum.

JVM bellek gereksinimleri söz konusu olduğunda, Java 8'i bekleyin, -Xmx belirtmeniz bile gerekmiyor, meta alan ayarları JVM bellek ihtiyacını karşılayacak ve otomatik olarak büyüyecek.


Herhangi bir cevabı oylamada bulunan kişilerin de yorum yazması çok yapıcı olacaktır. Ne de olsa hepimiz burada bilgiyi paylaşmak için varız ve uygun bir sebep olmadan karar vermek sadece bir amaca hizmet etmeyecek.
AKS

1
Yığın değişimi, bu cevaba yorum yapıldığında herhangi bir cevabı oy kullanan kullanıcıların bilgilendirilmesi için bazı mekanizmalara sahip olmalıdır.
AKS

1

Bellek ayırmaktan çok bir sınıf yaratmaktan daha fazlası var. Aynı zamanda ilklendirme de var, neden bu kısmın tüm cevaplar kapsamında olmadığından emin değilim. Normal sınıflar bazı değişkenler içerir ve bir tür başlatma şekli yapar ve bu ücretsiz değildir. Sınıfın ne olduğuna bağlı olarak, dosyaları okuyabilir veya başka yavaş işlemler yapabilir.

Bu yüzden, sınıf kurucusunun ne yaptığını göz önünde bulundurun, özgür olup olmadığını öğrenmeden önce.


2
İyi tasarlanmış bir sınıf, bu nedenle yapıcısında ağır kaldırma yapmamalıdır. Örneğin, dosya okuma sınıfınız, yalnızca başlangıçta hedef dosyasının var olduğunu doğrulayarak ve dosyadan veri gerektiren ilk yöntem çağrısına kadar gerçek dosya işlemlerini erteleyerek, başlatma maliyetini düşürebilir.
GordonM

2
Ek maliyet 'if (firstTime)' a taşınırsa, bir döngüde sınıfı yeniden oluşturmak, sınıfı yeniden kullanmaktan daha pahalıdır.
Alex,

Benzer bir cevap göndermek üzereydim ve bunun doğru cevap olduğuna inanıyorum. Pek çok insan, Java nesnesi oluşturmanın ucuz olduğunu söyleyenler tarafından kör ediliyor, çoğu zaman yapıcıların veya daha fazla başlatmanın ucuz olmaktan çok uzak olduğunu fark etmiyorlar. Örneğin, Swing'deki ImageIcon sınıfı, önceden yüklenmiş bir Image nesnesini geçseniz bile, yapıcı oldukça pahalıdır. @GordonM ile aynı fikirde değilim, JDK'dan birçok sınıf yapıcıdaki girişimin çoğunu gerçekleştiriyor ve kodun daha yalın, daha iyi tasarlanmış olduğunu düşünüyorum.
qwertzguy

1

Java’nın GC’si, hızlı bir şekilde “patlama” şeklinde hızlı bir şekilde çok sayıda nesne oluşturma açısından gerçekten optimize edilmiştir. Anlayabildiğim kadarıyla, bu tür bir "patlama döngüsü" için "Eden alanı" olarak adlandırdıkları bir bellek alanına sıralı bir ayırıcı (değişken boyutta talepler için en hızlı ve en basit O (1) ayırıcısı) kullanıyorlar ve sadece nesneler devam ederse Bir GC döngüsünden sonra, GC'nin onları birer birer toplayabileceği bir yere taşınırlar.

Bununla birlikte, performansınızın yeterince kritik hale gelmesi durumunda (gerçek kullanıcı sonu gereklilikleriyle ölçülen şekilde), nesneler bir yükü taşır, ancak oluşturma / tahsis etme konusunda fazla düşünmezdim. Bu yansıma ve dinamik sevk (gibi kavramları desteklemek için gerektiği gibi başvurusu konum ve Java tüm nesnelerin ek boyutu ile yapmak daha vardır Floatdaha büyük floatolan hizalama gereksinimleri ile 64 bit üzerine daha büyük 4 kat gibi bir şey genellikle, ve bir dizi Floatmutlaka anladığımdan bitişik olarak saklanacağı garanti edilmez).

Java'da geliştirdiğim en dikkat çekici şeylerden biri, kendi alanımdaki (VFX) ağır bir rakip olduğunu düşünmemi sağlayan interaktif, çok iş parçacıklı bir standart yol izleyiciydi (ışınım önbelleği veya BDPT veya MLS veya başka bir şey kullanmıyor). Gürültüsüz bir görüntüye oldukça hızlı bir şekilde yaklaşan gerçek zamanlı önizlemeler sağlayan CPU. C ++ 'daki profesyonellerle çalıştım, kariyerlerini ellerinde böyle yapmakta zorlanan süslü profilcilerle olan şeylere adadım.

Ancak kaynak koduna baktım ve önemsiz maliyetle çok sayıda nesne kullanırken, yol izleyicinin en kritik kısımları (BVH ve üçgenler ve materyaller), ilkel türlerin büyük dizileri lehine çok net ve kasten kaçındı. çoğunlukla float[]ve int[]), floatdiziden birinden diğerine geçmek için çok daha az bellek ve garantili mekansal konum kullandı . Yazarın beğendiği kutulu türleri kullanması durumunda bunu düşünmenin spekülatif olduğunu sanmıyorum.Floatorada, performansı için oldukça büyük bir maliyetle gelirdi. Ama biz bu motorun en kritik kısmından bahsediyoruz ve geliştiricinin ne kadar ustaca optimize ettiği göz önüne alındığında, bu optimizasyonu ne kadar ustaca kullandığı ve bu optimizasyonu çok ama çok ustaca kullandığı için eminim. etkileyici gerçek zamanlı yol izleyicisine önemsiz maliyet.

Bir meslektaşım tarafından Java nesnesi yaratmada gerçekleştirebileceğiniz en pahalı işlem olduğu söylendi. Bu yüzden sadece mümkün olduğu kadar az nesne oluşturmaya karar verdim.

Benimki kadar kritik performans gerektiren bir alanda bile, eğer önemli olmayan yerlere girerseniz, verimli ürünler yazmazsınız. Performans açısından en kritik alanların üretkenliğe daha da talep edebileceği iddiasını bile öne süreceğim, çünkü fazladan zaman harcamamamız gereken zaman harcayacağımız zamana ihtiyacımız var. . Yukarıdaki yukarıdaki yol izleyici örneğinde olduğu gibi, yazar ustaca ve ustaca bu tür optimizasyonları yalnızca gerçekten, gerçekten önemli olan ve muhtemelen ölçümden sonra en önemlisi ve her yerde mutlu bir şekilde kullanılan nesnelere uyguladı.


1
İlk paragrafınız doğru yolda. Eden ve genç alanlarda yakl. Ölü nesneler için 0 maliyet. Bu sunumu şiddetle tavsiye ediyorum . Bir yandan, bu sorunun 2012'den itibaren olduğunu biliyorsunuz, değil mi?
JimmyJames,

@JimmyJames Sıkıldım ve eskileri de içeren soruları incelemek istiyorum. Umarım insanlar benim endişelerime aldırmazlar! :-D
Dragon Energy,

@JimmyJames Artık kutulu türlerin ek bellek gereksinimlerine sahip olmaları ve bir dizide bitişik olmaları garanti edilmiyor mu? Ben nesneler Java ucuz kir olduğunu düşünüyorum eğilimindedir ama nispeten pahalı olabilir bir durum gibi float[]vs. Float[]eski sırayla bir milyon işlem göreceli olarak oldukça hızlı saniyeden daha olabilir.
Dragon Energy,

1
Burada ilk gönderim başladığımda da aynı şekilde yaptım. Sadece eski soruların cevapları çok az ilgi görmeye başladığında şaşırmayın.
JimmyJames

1
Kutulu türlerin, ilkellerden daha fazla alan kullanacağı varsayıldığından eminim. Bu konuda potansiyel olarak optimizasyonlar var ama genel olarak, açıkladığınız gibi olduğunu düşünüyorum. Gil Tene (yukarıdaki sunumda), yapıların bazı faydalarını sağlayacak, ancak yine de objeleri kullanan özel bir koleksiyon ekleme önerisi hakkında bir başka konuşma yaptı. Bu ilginç bir fikir, JSR'ın durumundan emin değilim. Maliyet, büyük ölçüde ne için uygun olduğun zamandan kaynaklanıyor. Nesnelerinizin 'kaçmasına' izin vermekten kaçınabilirseniz, yığın tahsis edilebilir ve yığına asla dokunulmaz.
JimmyJames

0

İnsanların nesnenin yaratılmasının Java'da büyük bir maliyet olmadığını söylediği gibi (ancak ekleme, vb. Gibi basit işlemlerden daha büyük bahse girerim) ve bundan çok fazla kaçınmamalısınız.

Bu hala bir maliyet olduğunu söyledi ve bazen kendinizi olabildiğince fazla nesneyi kazımaya çalışırken bulabilirsiniz. Ancak sadece profillemeden sonra bunun bir sorun olduğunu göstermiştir.

İşte konuyla ilgili harika bir sunum: https://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf


0

Bununla ilgili hızlı bir mikrobölge yaptım ve github'da tam kaynaklar sağladım. Benim sonucum, nesnelerin yaratılmasının pahalı olup olmadığının sorun olmadığı, ancak GC'nin sizin için dikkat edeceği düşüncesiyle sürekli olarak nesneler yaratmanın uygulamanızın GC sürecini daha erken tetiklemesini sağlayacağıdır. GC çok pahalı bir işlemdir ve mümkün olduğunda önlemek ve en iyisi tekmelemek için zorlamamaktır.


Bu, önceki 15 cevapta belirtilen ve açıklanan önemli noktalar üzerinde önemli bir şey sunmuyor gibi görünüyor. 2 yıldan fazla bir süre önce sorulmuş ve çok kapsamlı bir cevap
gnat
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.