- Di masa lalu, penggunaan CPU sistem saya pernah mencapai 3.200%, artinya semua 32 core terpakai penuh
- Saat itu saya menggunakan runtime Java 17, dan setelah memeriksa waktu CPU dari thread dump lalu mengurutkannya berdasarkan waktu CPU, ditemukan banyak thread serupa
- Analisis kode yang bermasalah
- Melalui stack trace, ditemukan baris ke-29 pada kelas
BusinessLogic
- Kode tersebut berbentuk iterasi atas daftar
unrelatedObjects sambil memasukkan nilai relatedObject ke treeMap
- Ini adalah kode yang tidak efisien karena
unrelatedObject tidak digunakan di dalam loop
Perbaikan Kode dan Pengujian
- Loop yang tidak perlu dihapus dan diubah menjadi satu baris:
treeMap.put(relatedObject.a(), relatedObject.b());
- Unit test dijalankan sebelum dan sesudah perbaikan, tetapi masalahnya tidak dapat direproduksi
- Bahkan saat ukuran
treeMap dan unrelatedObjects masing-masing melebihi 1.000.000 item, masalah tetap tidak muncul
Penyebab Masalah Ditemukan
treeMap ternyata diakses secara bersamaan oleh banyak thread, dan tidak disinkronkan
- Masalah ini terjadi karena beberapa thread memodifikasi
TreeMap secara bersamaan
Mereproduksi Masalah lewat Eksperimen
- Dilakukan eksperimen di mana beberapa thread memperbarui
TreeMap bersama secara acak
- Blok
try-catch digunakan agar NullPointerException diabaikan
- Hasil eksperimen menunjukkan penggunaan CPU bisa naik hingga 500%
Kesimpulan
- Modifikasi bersamaan pada
TreeMap yang tidak disinkronkan dapat menyebabkan masalah performa yang serius
- Untuk mencegah masalah seperti ini, disarankan menyinkronkan
TreeMap atau menggunakan koleksi yang thread-safe seperti ConcurrentMap
1 komentar
Komentar Hacker News
Saya kira race condition menyebabkan korupsi data atau deadlock, tetapi saya tidak terpikir bahwa hal itu juga bisa memicu masalah performa. Data bisa rusak dengan cara yang menciptakan loop tak berujung
Dalam kode yang dijalankan oleh beberapa thread, satu-satunya strategi yang benar-benar pasti adalah membuat semua objek menjadi immutable, dan untuk objek yang tidak bisa dibuat immutable, membatasinya ke bagian yang kecil, mandiri, dan dikendalikan dengan ketat
Penyebutan bahwa "hampir tidak bisa masuk lewat ssh" mengingatkan pada masa kuliah pascasarjana saat menggunakan Sun UltraSparc 170
Kodenya bisa disederhanakan menjadi seperti berikut
Cara lain untuk mendapatkan loop tak berujung adalah menggunakan implementasi <i>Comparator</i> atau <i>Comparable</i> yang tidak menerapkan total ordering yang konsisten
Bisa dipertimbangkan metode untuk mendeteksi siklus dengan memakai counter yang terus bertambah, lalu melempar exception jika melebihi kedalaman tree atau ukuran koleksi
Menjalankan operasi konkurensi pada objek yang tidak thread-safe di Java menghasilkan bug yang paling menarik
Ada pertanyaan apakah TreeMap yang tidak dilindungi bisa menyebabkan utilisasi 3.200%
Penulis menemukan salah satu jenis Poison Pill. Ini lebih umum di sistem event sourcing, berupa pesan yang membunuh apa pun yang ditemuinya
Exception di thread adalah masalah yang benar-benar serius