- Geçmişte sistemimde CPU kullanımı %3.200'e ulaştı; 32 çekirdeğin tamamı tamamen doluydu
- Java 17 çalışma zamanını kullanıyordum ve thread dump içinde CPU süresine bakıp CPU süresine göre sıralayınca benzer çok sayıda thread buldum
- Sorunlu kodun analizi
- Stack trace üzerinden
BusinessLogic sınıfının 29. satırını kontrol ettim
- İlgili kod,
unrelatedObjects listesini dolaşırken relatedObject değerini treeMap içine ekliyordu
- Bu, döngü içinde
unrelatedObject kullanılmadığı için verimsiz bir koddu
Kod düzeltmesi ve test
- Gereksiz döngü kaldırıldı ve kod
treeMap.put(relatedObject.a(), relatedObject.b()); satırına indirildi
- Değişiklikten önce ve sonra unit test çalıştırdım, ancak sorunu yeniden üretemedim
treeMap ve unrelatedObjects boyutları ayrı ayrı 1.000.000'un üzerinde olsa bile sorun ortaya çıkmadı
Sorunun nedeninin bulunması
treeMape birden fazla thread aynı anda erişiyordu ve senkronizasyon yoktu
- Sorun, birden fazla thread'in
TreeMap üzerinde aynı anda değişiklik yapmasından kaynaklanıyordu
Deneyle sorunu yeniden üretme
- Birden fazla thread'in paylaşılan
TreeMapi rastgele güncellediği bir deney yaptım
try-catch bloğu kullanarak NullPointerException hatasını yok sayacak şekilde ayarladım
- Deney sonucunda CPU kullanımının %500'e kadar çıktığını doğruladım
Sonuç
- Senkronize edilmemiş
TreeMap üzerinde eşzamanlı değişiklikler ciddi performans sorunlarına yol açabilir
- Bu tür sorunları önlemek için
TreeMapi senkronize etmek veya ConcurrentMap gibi thread-safe koleksiyonlar kullanmak önerilir
1 yorum
Hacker News görüşleri
Race condition'ların veri bozulmasına ya da deadlock'a yol açtığını düşünürdüm, ama performans sorunlarına da neden olabileceğini hiç düşünmemiştim. Veriler, sonsuz döngü oluşturacak şekilde bozulabilir
Collections.synchronizedMapile sarmak ya daConcurrentHashMap'e geçip gerektiğinde sıralamak en kolay çözümBirden fazla thread'in çalıştığı kodda, tüm nesneleri immutable yapmak ve immutable yapılamayan nesneleri küçük, kendi kendine yeten ve sıkı biçimde kontrol edilen bölümlerle sınırlamak tek güvenilir strateji
"ssh'e neredeyse bağlanamıyordum" ifadesi, yüksek lisans dönemimde Sun UltraSparc 170 kullandığım zamanları hatırlattı
Kod aslında basitçe şu hâle indirgenebilir
Sonsuz döngü elde etmenin bir başka yolu da tutarlı bir total order uygulamayan bir <i>Comparator</i> veya <i>Comparable</i> implementasyonu kullanmak
Artan bir sayaç kullanarak cycle tespiti yapmayı ve ağaç derinliğini ya da koleksiyon boyutunu aşarsa exception fırlatmayı düşünebilirsiniz
Java'da thread-safe olmayan nesneler üzerinde eşzamanlı işlemler yapmak, en ilginç bug'ları üretir
Korunmasız bir TreeMap'in %3.200 kullanım yaratıp yaratamayacağı soruluyor
Yazar, Poison Pill'in bir türünü keşfetmiş. Bu, event sourcing sistemlerinde daha yaygındır; karşılaştığı her şeyi öldüren bir mesajdır
Thread'lerdeki exception'lar mutlak bir problemdir