Masalah dan Skenario yang Berlaku
Sebuah API Java yang sensitif terhadap latensi berjalan di HotSpot JDK 25 dengan G1 dan heap tetap 12 GiB: -Xms12g -Xmx12g. Setelah fitur cache dirilis, p99 permintaan naik dari 120 milidetik menjadi 2–4 detik setiap 3–5 menit. CPU host tidak jenuh selama lonjakan tersebut. Pemantauan juga menunjukkan bahwa okupansi old-generation setelah pengumpulan naik dari 6,1 GiB menjadi 9,2 GiB selama dua jam, sementara laju alokasi aplikasi meningkat dari sekitar 600 MiB/s menjadi 1,4 GiB/s. Tim melabeli insiden tersebut sebagai "masalah GC" dan mengusulkan untuk meningkatkan heap menjadi 24 GiB terlebih dahulu.
Semua angka adalah asumsi wawancara. Kandidat harus menjelaskan cara membuktikan apakah lonjakan latensi benar-benar sesuai dengan jeda JVM; cara menggunakan pencatatan log GC terpadu (unified GC logging), Java Flight Recorder (JFR), dan diagnostik heap terbatas untuk membedakan laju alokasi tinggi, pertumbuhan live-set, objek humongous G1, System.gc() eksplisit, dan safepoint non-GC; serta cara menghasilkan rencana remediasi dan validasi yang dapat dibatalkan (reversible). Tujuannya bukan untuk menghafal flag pengumpul (collector). Tujuannya adalah membangun rantai sebab-akibat dari gejala ke bukti, hipotesis, eksperimen, dan SLO.
Materi wawancara GC Java publik memperlakukan peristiwa stop-the-world dan durasi jeda JVM sebagai topik inti. Panduan HotSpot Oracle untuk pemecahan masalah jeda panjang secara langsung mencakup heap yang tidak mencukupi, fragmentasi heap, aktivitas sistem operasi, dan GC eksplisit. Sumber-sumber tersebut menjadikan ini pertanyaan diagnostik performa JVM yang representatif. Tidak ada atribusi perusahaan yang andal secara publik, sehingga companyName dibiarkan kosong.
Apa yang Diuji oleh Pewawancara
Sinyal pertama adalah apakah kandidat menyelaraskan lini masa sebelum melakukan penyesuaian (tuning). P99 permintaan, log GC JVM, event JFR, CPU throttling kontainer, event disk, dan metrik host memerlukan basis waktu yang sama. Grafik heap sawtooth hanya membuktikan bahwa pengumpulan telah terjadi; grafik tersebut tidak membuktikan bahwa lonjakan permintaan tiga detik tertentu disebabkan oleh GC. Sebaliknya, G1 melakukan pekerjaan konkuren yang substansial, sehingga siklus GC yang panjang tidak berarti aplikasi dijeda selama seluruh siklus tersebut.
Sinyal kedua adalah apakah kandidat memisahkan jeda individual yang terlalu panjang dari rasio total jeda yang berlebihan. Yang pertama memerlukan jenis jeda dan pengaturan waktu fase, data live sebelum/sesudah, byte yang disalin, dan pengaturan waktu OS. Yang terakhir sering kali melacak laju alokasi dan frekuensi pengumpulan. Durasi GC rata-rata saja menyembunyikan jeda ekor (tail pauses) yang langka dan sejumlah besar jeda pendek.
Sinyal ketiga adalah apakah bukti menentukan tindakan remediasi. Tekanan alokasi memerlukan pencarian titik panas (hot spot) alokasi. Baseline old-after-GC yang meningkat memerlukan pembuktian bahwa objek yang tidak diinginkan tetap dapat dijangkau (reachable). Pertumbuhan dalam Humongous regions memerlukan penelusuran objek yang melebihi setengah region G1. Penyebab log berupa System.gc() memerlukan pencarian pemanggilnya. "Tingkatkan heap, turunkan target jeda, atau beralih ke ZGC" bukanlah diagnosis yang cocok untuk setiap gejala.
Terakhir, pewawancara menguji kesadaran risiko produksi. Statistik heap JFR memicu pengumpulan old tambahan. Oracle menandai jcmd GC.class_histogram dan heap dump sebagai operasi berdampak tinggi, dan heap dump meminta Full GC secara default. Jawaban yang kuat mengumpulkan bukti ber-overhead rendah terlebih dahulu, memperoleh bukti kelas berat pada replika, di luar jam sibuk, atau di lingkungan yang terkontrol, serta menggunakan canary dengan beban yang sama untuk membuktikan bahwa jeda yang lebih rendah tidak ditebus dengan throughput, CPU, atau memori yang tidak dapat diterima.
Pertanyaan yang Perlu Diklarifikasi Sebelum Menjawab
- Apa sebenarnya yang dimaksud dengan "stalled" (macet)? Apakah ini latensi penanganan server, latensi end-to-end klien, semua
utas Java tidak mengalami kemajuan, atau hanya beberapa permintaan yang mengantre? Apakah lonjakan terisolasi pada satu instans, dan apakah load balancer, dependensi, atau jaringan menunjukkan peristiwa yang sama?
- Dapatkah log dan metrik dikorelasikan secara tepat? Kita memerlukan ID instans, stempel waktu UTC, uptime JVM,
dan versi rilis yang sama. Jika log hanya memiliki stempel waktu tingkat menit, tingkatkan observabilitas sebelum menarik kesimpulan dari dua kurva yang hanya terlihat serupa.
- Apa isi log G1? Minimal, pertahankan GC ID, penyebab, jenis jeda, heap sebelum dan sesudah,
dan durasi. Aktifkan gc+heap, gc+phases, dan gc+cpu jika diperlukan. RSS proses atau persentase heap saja tidak cukup.
- Apakah baseline old-generation setelah pengumpulan terus meningkat di bawah beban yang sebanding? Titik stabil (plateau)
setelah pemanasan cache (cache warm-up) mungkin merupakan live-set yang diharapkan. Pertumbuhan berkelanjutan dengan byte yang direklamasi semakin berkurang lebih konsisten dengan retensi atau kebocoran memori (memory leak).
- Batasan apa yang berlaku untuk kontainer dan host? Periksa kuota dan throttling CPU, swap, page fault,
tekanan memori, I/O log yang memblokir, dan noisy neighbors. Waktu wall-clock GC yang panjang dengan waktu CPU yang sedikit mungkin berarti JVM mengalami descheduling atau halamannya di-swap out.
- Hubungan alokasi dan referensi mana yang berubah dalam rilis tersebut? Periksa kapasitas cache,
TTL, ukuran kunci dan nilai, buffer serialisasi, ukuran batch, konkurensi, dan retensi melalui ThreadLocals, listener, antrean, atau koleksi statis.
- Apa target latensi dan throughput? Tentukan jeda p99 dan maksimum, p99 permintaan, throughput,
tingkat kesalahan, CPU, dan batas memori. -XX:MaxGCPauseMillis adalah petunjuk target untuk G1, bukan batas mutlak pada setiap jeda.
- Dapatkah bukti kelas berat dikumpulkan dengan aman? Jika hanya ada satu instans produksi, tambahkan
kapasitas atau kurangi lalu lintas terlebih dahulu. Jangan mendump heap 12 GiB selama insiden dan kemudian salah mengira Full GC diagnostik sebagai kegagalan asli.
Kerangka Kerja Jawaban 30 Detik
"Saya tidak akan langsung mengubah ukuran heap. Saya akan mengorelasikan setiap lonjakan permintaan, berdasarkan instans dan stempel waktu, dengan -Xlog:gc*, event JFR jdk.GCPhasePause, CPU throttling, page fault, dan latensi dependensi untuk mengukur berapa lama aplikasi benar-benar berhenti. Kemudian saya akan membagi bukti menjadi tiga kelompok: jenis dan fase dari setiap jeda; rasio total jeda per jendela waktu; serta tren laju alokasi, laju promosi, okupansi old-after-GC, dan region humongous.
Jika cache meningkatkan alokasi tetapi baseline pasca-pengumpulan stabil, saya akan menggunakan JFR untuk menemukan titik panas alokasi dan mengurangi objek sementara. Jika old-after-GC terus meningkat, saya akan membandingkan histogram kelas dan mengambil heap dump pada replika yang terkontrol untuk memeriksa dominator dan jalur retensi. Jika Full GC didahului oleh kegagalan evakuasi atau banyak region humongous, saya akan memeriksa dan membagi array besar, buffer, dan batch. Jika penyebabnya adalah System.gc(), saya akan mengidentifikasi pemanggilnya. Setiap perubahan akan diuji dalam canary berbeban sama terhadap p99 permintaan, jeda p99 dan maksimum, rasio total jeda, laju alokasi, live-set pasca-pengumpulan, CPU, throughput, dan error sebelum peluncuran."
Pembahasan Mendalam Langkah demi Langkah
Langkah 1: Bangun lini masa yang dapat diuji kesalahannya (falsifiable) dan buktikan apakah GC terlibat.
Untuk setiap lonjakan latensi, catat instans, jendela permintaan, versi rilis, dan waktu UTC. Korelasikan dengan pencatatan log terpadu dan event jeda JFR berdasarkan GC ID. Konfigurasi startup baseline dapat mempertahankan data GC dan safepoint bertanda waktu dan berlabel dalam file yang berputar (rotating files):
-Xlog:gc*,safepoint:file=/var/log/app/gc-%p.log:time,uptime,level,tags:filecount=10,filesize=100mIni adalah contoh diagnostik; sesuaikan jalur, retensi, dan anggaran disk dengan lingkungan Anda. Di JFR, fokuslah pada durasi jdk.GCPhasePause sambil memeriksa beban CPU, utas, I/O soket/file, dan event alokasi. Panduan Oracle mencatat bahwa, untuk pengumpul konkuren, panjang siklus total kurang bermakna dibandingkan waktu di mana aplikasi benar-benar dijeda.
Buat tiga cabang hasil. Jika lonjakan selaras satu-ke-satu dengan jeda JVM, lanjutkan ke analisis akar penyebab GC. Jika jeda GC yang dilaporkan pendek tetapi safepoint panjang, periksa alasan safepoint dan waktu untuk mencapainya. Jika keduanya tidak selaras, selidiki CPU throttling, lock, I/O, jaringan, dan layanan hilir (downstream). Ini membuat hipotesis GC dapat diuji kesalahannya, bukan menjadi penjelasan default untuk semua masalah latensi.
Langkah 2: Jelaskan gejala GC dengan serangkaian metrik, bukan satu grafik saja.
Pertahankan setidaknya deret waktu berikut dan bandingkan jendela beban yang sama sebelum dan sesudah rilis:
Requests: p50 / p95 / p99 / max, throughput, timeouts, error rate
Pauses: pause p50 / p95 / p99 / max, paused time per minute, pause cause
Heap: young / old usage, old-after-GC, reclaimed bytes, promotion rate
Allocation: bytes/s, top allocation sites by class and thread, inside/outside TLAB
G1: young / mixed / Full counts, evacuation failures, humongous regions
System: process CPU, GC CPU, CPU throttling, RSS, swap, major page faults, disk latencyFormat used-before → used-after (heap-capacity) menunjukkan apa yang direklamasi oleh satu pengumpulan, tetapi satu titik bukanlah sebuah tren. Baseline old-after-GC yang meningkat secara monoton pada beban yang sebanding menunjukkan pertumbuhan dalam live-set atau tekanan promosi. Baseline yang stabil dengan pengumpulan yang semakin sering lebih sering mengarah pada alokasi tinggi. Ketika gc+cpu=info menunjukkan waktu nyata (real time) jauh di atas waktu user ditambah system, selidiki penjadwalan, kuota, atau paging sebelum menambahkan utas GC.
Langkah 3: Gunakan bukti untuk memisahkan lima jalur akar masalah.
- Laju alokasi tinggi. Old-after-GC stabil, sementara jumlah jeda young dan waktu jeda per menit
meningkat. Event alokasi JFR mengarah pada konstruksi kunci cache, serialisasi, atau koleksi sementara. Kurangi penyalinan, gunakan kembali buffer dengan siklus hidup yang jelas, dan kendalikan ukuran batch. Jangan memperkenalkan pooling objek secara luas dan shared state tanpa bukti.
- Pertumbuhan live-set atau kebocoran memori. Old-after-GC terus meningkat dan setiap pengumpulan mereklamasi lebih sedikit memori.
Bandingkan histogram kelas pada beberapa waktu. Ambil heap dump hanya pada replika di luar jam sibuk atau di lingkungan traffic-replay, lalu gunakan retained size, dominator tree, dan GC root untuk membedakan cache yang sah dari retensi tanpa batas. Cache yang stabil pada kapasitasnya adalah masalah ukuran (sizing); objek kedaluwarsa yang tetap direferensikan mengindikasikan kebocoran.
- Objek humongous G1. G1 memperlakukan objek berukuran setidaknya setengah region sebagai humongous dan menempatkannya
langsung di region old yang berdekatan. Jika Humongous regions: X → Y tetap tinggi, telusuri byte[] besar, char[], blok terkompresi, atau batch, dan kurangi ukuran objek individual atau batch terlebih dahulu. Perlakukan perubahan ukuran region sebagai eksperimen hanya setelah pengukuran, karena hal itu juga mengubah jumlah region dan granularitas pengumpulan.
- Penandaan konkuren tertinggal atau evakuasi gagal. Jika log menunjukkan ruang tujuan yang tidak mencukupi
atau region yang tidak dapat dievakuasi, skenario terburuknya adalah Full GC seluruh heap. Kurangi alokasi atau promosi region old, berikan headroom yang cukup untuk penandaan konkuren, dan periksa margin keamanan antara kapasitas heap dan live-set. Menurunkan MaxGCPauseMillis saja dapat membuat setiap pengumpulan bekerja lebih sedikit dan membuat sistem semakin dekat dengan kehabisan memori.
- GC eksplisit atau safepoint non-GC. Jika penyebab GC adalah
System.gc(), gunakan call stack, konfigurasi
dependensi, dan perintah operasional untuk menemukan sumbernya sebelum menghapus panggilan tersebut, mengonfigurasi pustaka, atau mengisolasi tugas. Gunakan DisableExplicitGC hanya setelah memeriksa dampak semantik. Jika safepoint panjang tetapi jeda GC pendek, periksa penyebab sebenarnya—seperti deoptimasi, redefinisi kelas, atau operasi VM lainnya—daripada mengganti pengumpul.
Langkah 4: Buat lapisan alat diagnostik dan kendalikan risiko observasi.
Mulailah dengan lapisan berbiaya lebih rendah yang tersedia secara terus-menerus: SLI aplikasi, log GC/safepoint terpadu, dan JFR standar. Untuk konfirmasi langsung, jalankan jcmd PID JFR.start, JFR.check, dan JFR.dump pada host yang sama sebagai pengguna efektif yang sama. Periksa opsi yang didukung oleh JVM tersebut terlebih dahulu dengan jcmd PID help COMMAND.
Kemudian beralih ke lapisan berbiaya tinggi. Oracle menandai GC.class_histogram sebagai berdampak tinggi. GC.heap_dump juga berdampak tinggi dan meminta Full GC secara default. Statistik heap JFR memicu pengumpulan old ekstra di awal dan akhir, jadi jangan aktifkan statistik heap secara default selama investigasi latensi. Kuras lalu lintas atau reproduksi masalah pada replika, konfirmasikan kapasitas disk dan penanganan data sensitif, dan baru setelah itu ambil histogram atau dump. Membandingkan dua tangkapan yang dipisahkan oleh interval beban yang stabil menjelaskan pertumbuhan dengan lebih baik daripada satu daftar objek terbesar.
Langkah 5: Pertahankan kausalitas tunggal saat mengubah kode, kapasitas, atau pengaturan pengumpul.
Mulailah dengan cache baru. Apakah tidak terbatas? Apakah TTL-nya benar-benar menghapus entri yang kedaluwarsa? Apakah bobot maksimum diukur dalam byte dan bukan jumlah entri? Apakah setiap nilai menyalin array besar? Apakah cache miss yang konkuren membangun nilai yang sama secara independen? Perbaikan kode kandidat meliputi pembatasan bobot maksimum, penggabungan (coalescing) pemuatan kunci yang sama, serialisasi streaming, pengurangan ukuran batch, atau pelepasan referensi ke objek sementara lebih cepat. Ubah satu variabel utama dalam setiap eksperimen.
Heap yang lebih besar dapat meningkatkan interval antar pengumpulan, tetapi juga dapat menyembunyikan retensi tanpa batas, meningkatkan risiko memori kontainer, dan meningkatkan jumlah data live yang pada akhirnya harus diproses. Sebelum mengubah IHOP, ukuran young-generation, ukuran region, atau target jeda, identifikasi fase yang tercatat dalam log atau defisit sumber daya yang diharapkan akan diubah oleh flag tersebut. Beralih ke pengumpul seperti ZGC adalah eksperimen arsitektural yang memerlukan validasi throughput, CPU, headroom heap, kontainer, dan operasional yang baru. Ini bukan tindakan darurat pertama saat insiden.
Langkah 6: Tutup insiden dengan canary berbeban sama dan bukti kontrafaktual.
Putar ulang permintaan berbentuk produksi, ukuran objek, hit rate cache, dan konkurensi terhadap versi baseline dan versi yang telah diperbaiki. Canary harus mencakup beberapa siklus lonjakan 3–5 menit yang asli dan mencakup cold start serta steady state cache. Tentukan ambang batas penerimaan sebelum pengujian, misalnya: p99 permintaan di bawah 200 ms; jeda GC p99 di bawah 100 ms dan maksimum di bawah 500 ms; kurang dari 1% waktu jeda per menit; old-after-GC tidak lagi meningkat setelah pemanasan; dan tidak ada regresi dalam throughput, anggaran CPU, OOM, atau error.
Kemudian uji kondisi kontrafaktual. Apakah me-rollback cache memulihkan laju alokasi dan lonjakan tersebut? Apakah membatasi bobot cache saja menstabilkan old-after-GC? Apakah hanya mengurangi ukuran batch mengurangi region humongous? Ketika metrik yang diprediksi bergerak bersama-sama, remediasi memiliki hubungan kausal dengan akar masalah. Luncurkan secara bertahap dan pertahankan kriteria rollback otomatis.
Contoh Jawaban Berkualitas Tinggi
"Data saat ini membuat GC layak diselidiki, tetapi data tersebut tidak membuktikan bahwa GC menyebabkan lonjakan permintaan 2–4 detik. Saya pertama-tama akan menyelaraskan SLI permintaan, -Xlog:gc*,safepoint, event JFR jdk.GCPhasePause, CPU throttling, page fault, dan latensi dependensi berdasarkan instans dan waktu UTC. Jika lonjakan selaras dengan jeda, saya akan membedakan satu jeda panjang dari banyak jeda pendek yang terakumulasi. Jika hanya safepoint yang panjang, saya akan mengikuti penyebab safepoint. Jika keduanya tidak selaras, saya akan meninggalkan cabang GC.
Rilis tersebut meningkatkan alokasi dari 600 MiB/s menjadi 1,4 GiB/s sementara old-after-GC naik dari 6,1 GiB menjadi 9,2 GiB. Itu memberi saya setidaknya dua hipotesis: cache menciptakan alokasi sementara yang substansial, dan cache atau objek terkait memperluas live-set. Saya akan memeriksa penyebab young, mixed, dan Full; heap sebelum dan sesudah; promosi; kegagalan evakuasi; dan region humongous. Event alokasi JFR mengidentifikasi kelas, utas, dan call site. Histogram dari beberapa waktu menunjukkan kelas mana yang terus tumbuh. Saya akan mengambil heap dump hanya pada replika yang telah dikosongkan dari lalu lintas atau dalam pengujian ulang karena dampaknya tinggi dan dapat meminta Full GC secara default.
Jika baseline pasca-pengumpulan stabil tetapi jeda young sering terjadi, saya akan mengurangi alokasi dalam konstruksi kunci cache, serialisasi, dan koleksi sementara. Jika baseline terus meningkat, saya akan menggunakan retained size dan jalur GC-root untuk membedakan cache terbatas dari kebocoran memori. Jika array besar melebihi setengah region G1 dan penggunaan region humongous tumbuh, saya akan membagi buffer atau batch. Jika penyebabnya adalah System.gc(), saya akan menemukan pemanggilnya. Jika waktu nyata jauh melebihi waktu CPU GC, saya akan menyelidiki kuota, swap, dan perebutan sumber daya host (host contention).
Saya tidak akan menganggap heap 24 GiB sebagai solusinya. Saya akan menguji setiap kandidat perubahan secara independen dalam canary berbeban sama di seluruh cold start cache dan beberapa siklus lonjakan asli. Evaluasi akhir mencakup p99 permintaan, jeda p99 dan maksimum, rasio total jeda, laju alokasi, old-after-GC, region humongous, throughput, CPU, RSS, error, dan OOM. Hanya setelah itu saya akan meningkatkan lalu lintas, dengan opsi rollback tetap dipertahankan."
Kesalahan Umum
- **Kesalahan: menyatakan GC sebagai penyebab setelah melihat grafik heap sawtooth → Mengapa gagal: grafik membuktikan
pengumpulan telah terjadi, bukan bahwa lonjakan permintaan dan jeda stop-the-world tumpang tindih pada satu instans → Koreksi: korelasikan permintaan, log, dan JFR dengan instans, GC ID, dan satu lini masa.**
- **Kesalahan: hanya melihat durasi GC rata-rata → Mengapa gagal: rata-rata menyembunyikan satu peristiwa ekor tiga detik
dan mengabaikan total biaya dari banyak jeda pendek → Koreksi: ukur persentil jeda, maksimum, waktu jeda per menit, dan penyebabnya.**
- **Kesalahan: memperlakukan durasi siklus GC sebagai durasi jeda aplikasi → Mengapa gagal: sebagian besar pekerjaan
penandaan G1 dapat berjalan secara konkuren dengan aplikasi → Koreksi: gunakan event jeda yang sebenarnya dan SLI permintaan.**
- **Kesalahan: meningkatkan heap dari 12 GiB menjadi 24 GiB selama insiden → Mengapa gagal: ini dapat menyembunyikan
retensi tanpa batas dan meningkatkan risiko memori tanpa membuktikan bahwa live-set memiliki batas yang valid → Koreksi: pisahkan laju alokasi dari pertumbuhan live-set pasca-pengumpulan, lalu jalankan eksperimen kapasitas yang dapat dibatalkan.**
- **Kesalahan: langsung mengambil heap dump pada satu-satunya instans produksi → Mengapa gagal: perintah tersebut
berdampak tinggi, dapat meminta Full GC secara default, menulis file besar, dan mengekspos data sensitif → Koreksi: kurangi lalu lintas dan kumpulkan pada replika atau pengujian ulang yang terkontrol.**
- **Kesalahan: mengubah ukuran region G1 setelah melihat objek besar → Mengapa gagal: objek besar mungkin tidak
melewati ambang batas setengah region, dan flag tersebut mengubah granularitas region di seluruh heap → Koreksi: konfirmasikan objek humongous dengan gc+heap dan bukti alokasi, lalu bandingkan eksperimennya.**
- **Kesalahan: memperlakukan
-XX:MaxGCPauseMillis=50sebagai SLA → Mengapa gagal: ini adalah petunjuk target dan G1 bukanlah
pengumpul real-time → Koreksi: validasi distribusi jeda riil dan anggarkan kompromi (tradeoff) antara target, throughput, dan headroom heap.**
- **Kesalahan: menonaktifkan GC eksplisit secara global begitu
System.gc()muncul → Mengapa gagal: dependensi
atau alur kerja operasional mungkin bergantung pada semantik tersebut → Koreksi: identifikasi pemanggil dan tujuannya sebelum menghapus, mengonfigurasi, atau mengisolasinya.**
- **Kesalahan: hanya memeriksa bahwa p99 turun setelah beralih pengumpul → Mengapa gagal: jeda yang lebih rendah mungkin
disertai dengan CPU yang lebih tinggi, throughput yang lebih rendah, atau memori yang lebih banyak → Koreksi: uji latensi, throughput, CPU, RSS, error, dan pemulihan di bawah beban yang sama.**
Pertanyaan Lanjutan dan Tanggapan
Lanjutan 1: Bagaimana Anda membedakan kebocoran memori dari pemanasan cache normal?
Periksa live-set setelah beberapa pengumpulan old pada beban yang sebanding, bukan hanya kemiringan awal setelah proses dimulai. Cache terbatas yang normal akan stabil (plateau) setelah mencapai bobot maksimum dan hit rate yang stabil, serta penggusuran (eviction) dan kedaluwarsa TTL harus dapat diamati. Kebocoran membiarkan objek tanpa nilai bisnis tetap dapat dijangkau dari GC root, sehingga baseline terus meningkat. Gunakan histogram kelas dari beberapa waktu untuk menemukan kelas yang berkembang, lalu periksa dominator, retained size, dan jalur referensi dalam heap dump yang terkontrol. Bahkan jika cache akhirnya mendatar, platform 9,2 GiB dalam heap 12 GiB mungkin menyisakan headroom yang tidak memadai untuk penandaan konkuren dan lonjakan beban (bursts), menjadikannya masalah desain kapasitas.
Lanjutan 2: Mengapa tidak menurunkan MaxGCPauseMillis terlebih dahulu?
Flag ini memandu seberapa banyak pekerjaan yang dicoba dilakukan G1 per pengumpulan; ini bukan batas atas yang dipaksakan. Jika live-set terlalu besar, penandaan konkuren tertinggal, atau kontainer tidak dapat menyediakan CPU, target yang lebih rendah dapat membuat setiap pengumpulan mixed mereklamasi lebih sedikit, meningkatkan frekuensi, dan membawa proses lebih dekat ke kegagalan evakuasi. Pertama, bentuk hipotesis dari pengaturan waktu fase, headroom heap, dan laju alokasi atau promosi, lalu validasi satu perubahan flag di bawah beban yang sama.
Lanjutan 3: Apa yang diindikasikan oleh waktu nyata (real time) yang panjang tetapi waktu user dan system yang pendek dalam log GC?
Ini menunjukkan bahwa utas GC tidak mengonsumsi CPU selama seluruh interval wall-clock. Kemungkinan penyebabnya meliputi CPU throttling kontainer, perebutan sumber daya host, swap atau major page fault, jeda virtualisasi, dan I/O log. Periksa kuota cgroup dan waktu yang ter-throttle, run queue, page fault, swap, latensi disk, dan event pada host yang digunakan bersama. Menambahkan utas GC paralel dapat memperparah perebutan sumber daya; atasi bukti pasokan sumber daya eksternal terlebih dahulu.
Lanjutan 4: Kapan Anda akan mempertimbangkan untuk beralih dari G1 ke ZGC?
Pertimbangkan hal ini ketika layanan memiliki target latensi rendah yang eksplisit, pertumbuhan alokasi dan live-set terkendali, G1 masih gagal memenuhi SLO jeda pada heap dan beban target, serta tim dapat memvalidasi kebutuhan tambahan CPU, headroom heap, kompatibilitas JDK, dan operasional. Jalankan uji A/B dengan lalu lintas berbentuk produksi, termasuk cold start, steady state, burst, pemulihan kegagalan, dan batasan kontainer. Migrasi pengumpul adalah pilihan kapasitas dan model runtime; ini tidak menggantikan perbaikan kebocoran objek atau cache yang tidak terbatas.
Lanjutan 5: Bagaimana Anda membuktikan perbaikan tersebut bertahan lama dan bukan hanya menunda lonjakan?
Jalankan canary cukup lama untuk mencakup beberapa siklus lonjakan asli, kondisi steady state cache, dan beban puncak yang diharapkan. Bandingkan kemiringan old-after-GC, Full GC per jam dan kegagalan evakuasi, laju alokasi, region humongous, rasio total jeda, dan SLI permintaan, serta uji di luar perkiraan puncak untuk memastikan tersedianya headroom. Jika heap yang lebih besar hanya memindahkan lonjakan pertama dari lima menit ke sepuluh menit sementara kemiringan baseline tidak berubah, remediasi tersebut telah gagal.