LinkedList daha hızlı olacak mı? 'Her biri için' ifadesini bir 'yineleyici' ile değiştirmeli miyim? Bu 'ArrayList' bir 'Array' mi olmalı? Bu makale o kadar kötü niyetli bir optimizasyona yanıt olarak geldi ki, hafızama kalıcı olarak kazındı.
Java'ya ve çöp toplayıcıdan ya da bağlam değiştirmeden kaynaklanan parazitlerle başa çıkmanın yollarına girmeden önce, ilk olarak gelecekteki kendiniz için kod yazmanın temellerine bir göz atalım.
Erken optimizasyon tüm kötülüklerin köküdür.
Bunu daha önce duymuştunuz; Erken optimizasyon tüm kötülüklerin köküdür. Bazen. Yazılım yazarken şunlara kesinlikle inanıyorum:
mümkün olduğunca açıklayıcı ; hikaye yazıyormuş gibi niyetleri anlatmaya çalışmalısınız.
mümkün olduğu kadar optimal ; bu da dilin temellerini bilmeniz ve buna göre uygulamanız gerektiği anlamına gelir.
Kodunuz amacı ifade etmelidir ve bunların büyük bir kısmı, yöntemleri ve değişkenleri adlandırma şeklinizle ilgilidir.
int[10] array1; // bad int[10] numItems; // better int[10] backPackItems; // great
Yalnızca değişken adından zaten işlevsellik sonucunu çıkarabilirsiniz.
numItems
soyut olsa da backPackItems
size beklenen davranış hakkında çok şey anlatır.
Veya bu yönteme sahip olduğunuzu söyleyin:
List<Countries> visitedCountries() { if(noCountryVisitedYet) return new ArrayList<>(0); } // (...) return listOfVisitedCountries; }
Kod söz konusu olduğunda, bu aşağı yukarı iyi görünüyor.
Daha iyisini yapabilir miyiz? Kesinlikle yapabiliriz!
List<Countries> visitedCountries() { if(noCountryVisitedYet) return Collections.emptyList(); } // (...) return listOfVisitedCountries; }
Collections.emptyList()
i okumak new ArrayList<>(0);
'dan çok daha açıklayıcıdır.
Yukarıdaki kodu ilk kez okuduğunuzu ve kullanıcının ülkeleri gerçekten ziyaret edip etmediğini kontrol eden koruma maddesine rastladığınızı hayal edin. Ayrıca, bunun uzun bir sınıfa gömüldüğünü hayal edin, Collections.emptyList()
okumak kesinlikle new ArrayList<>(0)
okumaktan daha açıklayıcıdır, ayrıca müşteri kodunun onu değiştiremeyeceğinden emin olarak bunun değişmez olduğundan da emin olursunuz.
Dilinizi bilin ve ona göre kullanın. Bir double
ihtiyacınız varsa, onu bir Double
nesnesine sarmanıza gerek yoktur. Eğer gerçekten ihtiyacınız olan tek şey Array
ise aynı durum List
kullanımı için de geçerlidir.
İş parçacıkları arasında durumu paylaşıyorsanız, StringBuilder
veya StringBuffer
kullanarak Dizeleri birleştirmeniz gerektiğini bilin:
// don't do this String votesByCounty = ""; for (County county : counties) { votesByCounty += county.toString(); } // do this instead StringBuilder votesByCounty = new StringBuilder(); for (County county : counties) { votesByCounty.append(county.toString()); }
Veritabanınızı nasıl indeksleyeceğinizi bilin. Darboğazları önceden tahmin edin ve buna göre önbellekleyin. Yukarıdakilerin tümü optimizasyonlardır. Bunlar, ilk vatandaşlar olarak bilmeniz ve uygulamanız gereken türden optimizasyonlardır.
Birkaç yıl önce okuduğum bir hack'i asla unutmayacağım. Gerçeği söylemek gerekirse yazar hızla geri adım attı ama bu, iyi niyetlerden ne kadar çok kötülüğün kaynaklanabileceğini gösteriyor.
// do not do this, ever! int i = 0; while (i<10000000) { // business logic if (i % 3000 == 0) { //prevent long gc try { Thread.sleep(0); } catch (Ignored e) { } } }
Cehennemden gelen bir çöp toplayıcı hack'i!
Yukarıdaki kodun neden ve nasıl çalıştığı hakkında daha fazla bilgiyi orijinal makalede okuyabilirsiniz ve bu istismar kesinlikle ilginç olsa da, bu asla yapmamanız gereken şeylerden biridir.
Thread.sleep(0)
ın bu blokta hiçbir amacı yoktur
Dilin sağladığı tüm varsayılan optimizasyonlarla yazdıktan sonra bir darboğazla karşılaşırsanız, biraz daha karmaşık bir şey oluşturmaya başlayın. Ancak yukarıdaki gibi karışımlardan uzak durun.
Her şey bittiğinde, Çöp Toplayıcı hâlâ direnç sunan parçaysa deneyebileceğiniz şeylerden bazıları şunlardır:
Hizmetiniz GC'ye izin veremeyecek kadar gecikmeye duyarlıysa, "Epsilon GC" ile çalıştırın ve GC'den tamamen kaçının .
-XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC
Bu, bir OOM istisnası elde edene kadar belleğinizi açıkça büyütecektir, yani bu ya kısa ömürlü bir senaryodur ya da programınız nesneler oluşturmayacak şekilde optimize edilmiştir
Hizmetiniz gecikmeye biraz duyarlıysa ancak izin verilen tolerans bir miktar hareket alanına izin veriyorsa , GC1'i çalıştırın ve -XX:MaxGCPauseTimeMillis=100
gibi bir şey besleyin (varsayılan 250 ms'dir)
Sorun harici kütüphanelerden kaynaklanıyorsa , diyelim ki bunlardan biri dünyayı durduran çöp toplayıcıları System.gc()
veya Runtime.getRuntime().gc()
yi çağırıyor, -XX:+DisableExplicitGC
ile çalıştırarak rahatsız edici davranışları geçersiz kılabilirsiniz. -XX:+DisableExplicitGC
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC
. Ayrıca bu JDK 21 GC karşılaştırmasını da kontrol etmek isteyebilirsiniz.
VERSİYON BAŞLANGICI | VERSİYON SONU | VARSAYILAN GC |
---|---|---|
Java 1 | Java4 | Seri Çöp Toplayıcı |
Java 5 | Java 8 | Paralel Çöp Toplayıcı |
Java 9 | devam ediyor | G1 Çöp Toplayıcı |
Not 1: Java 15'ten beri ZGC
üretime hazırdır , ancak yine de onu -XX:+UseZGC
ile açıkça etkinleştirmeniz gerekir.
Not 2: VM, ikiden fazla işlemci ve yığın boyutunun 1792 MB'a eşit veya daha büyük olduğunu tespit ederse, makineleri sunucu sınıfı olarak kabul eder. Sunucu sınıfı değilse, varsayılan olarak Serial GC olacaktır .
Temel olarak, uygulamanın performans kısıtlamalarının doğrudan çöp toplama davranışına bağlı olduğu açık olduğunda ve bilinçli ayarlamalar yapmak için gerekli uzmanlığa sahip olduğunuzda GC ayarlamasını tercih edin. Aksi takdirde, JVM'nin varsayılan ayarlarına güvenin ve uygulama düzeyindeki kodu optimize etmeye odaklanın.
u/shiphe - yorumun tamamını okumak isteyeceksiniz
Gerçek bir kıyaslama olmadan duyguyu optimize ediyorsanız, kendinize kötülük yapıyorsunuz demektir. JMH, algoritmalarınızın performansını test etmek için kullanılan fiili Java kütüphanesidir. Kullan onu.
Bir işlemi belirli bir çekirdeğe sabitlemek önbellek isabetlerini iyileştirebilir. Bu, temeldeki donanıma ve rutininizin verilerle nasıl başa çıktığına bağlı olacaktır. Bununla birlikte, bu kitaplık uygulamayı o kadar kolaylaştırır ki, yoğun CPU kullanan bir yöntem sizi zorluyorsa, onu test etmek isteyeceksiniz.
Bu, ihtiyacınız olmasa bile çalışmak isteyeceğiniz kütüphanelerden biri. Buradaki fikir, ultra düşük gecikme süreli eşzamanlılığa izin vermektir. Ancak mekanik sempatiden halka tamponuna kadar uygulanma şekli birçok yeni konsepti beraberinde getiriyor. Yedi yıl önce onu ilk keşfettiğim zamanı, onu sindirmek için bütün geceyi geçirdiğimi hâlâ hatırlıyorum.
jvmquake
temeli, JVM'de işler ters gittiğinde onun ölmesini ve takılmamasını istemenizdir. Birkaç yıl önce, sıkı bellek kısıtlamalarına sahip bir HTCondor kümesi üzerinde simülasyonlar çalıştırıyordum ve bazen işler "bellek yetersiz" hataları nedeniyle takılıp kalıyordu.
Bu kütüphane JVM'yi ölmeye zorlayarak gerçek hatayla başa çıkmanıza olanak tanır. Bu özel durumda, HTCondor işi otomatik olarak yeniden planlayacaktır.
Bu yazıyı yazmamı sağlayan kod? Çok daha kötülerini yazdım. Hala yapıyorum. Umabileceğimiz en iyi şey sürekli olarak daha az karışıklık yaratmaktır.
Birkaç yıl sonra kendi koduma baktığımda hoşnutsuz olmayı bekliyorum.
Ve bu iyi bir işaret.
visitedCountries()
'teki değişkenlik hatasını yakaladığı ve ayrıntılı açıklaması için FlorianSchaetz'e teşekkür ederiz.Wasteofserver.com'da da yayınlandı