Performa aplikasi Node di production ditentukan oleh seberapa cepat event loop memproses peristiwa dan seberapa besar memori yang dipakai sepanjang waktu. Profiling CPU dan heap digunakan untuk menemukan fungsi yang lambat serta objek yang tidak dibebaskan, sedangkan load testing mengukur throughput dan latensi pada beban yang menyerupai production. Topik ini membahas process.memoryUsage() sebagai alat dasar pemantauan memori, flag --cpu-prof dan --heap-prof bawaan Node untuk merekam jejak, serta konfigurasi load testing dengan autocannon. Setelah membaca topik ini, pembaca dapat mendeteksi kebocoran memori sederhana, menjalankan profiling dasar, serta menyusun konfigurasi load testing untuk mengukur kapasitas aplikasi.
Memantau memori dengan process.memoryUsage#
Fungsi process.memoryUsage() mengembalikan objek dengan lima field: rss (resident set size, yaitu total memori proses termasuk shared libraries), heapTotal (memori heap yang dialokasikan V8), heapUsed (memori heap yang benar-benar dipakai), external (memori di luar heap yang dikelola V8, seperti Buffer), dan arrayBuffers (memori yang dialokasikan untuk ArrayBuffer dan Buffer). Pemanggilan berkala fungsi ini memberi gambaran tren memori, yang berguna untuk mendeteksi kebocoran.
// tidak-dijalankan: jalankan dengan `node --expose-gc leak-demo.js` agar keluaran stabil
const cache = [];
function snapshot(iterasi) {
if (global.gc) global.gc();
const m = process.memoryUsage();
const mb = (b) => Math.round(b / 1024 / 1024);
console.log(`iterasi ${iterasi}: heapUsed=${mb(m.heapUsed)}MB external=${mb(m.external)}MB arrayBuffers=${mb(m.arrayBuffers)}MB`);
}
snapshot(0);
for (let iterasi = 1; iterasi <= 5; iterasi++) {
for (let i = 0; i < 100000; i++) {
cache.push({ ts: Date.now(), data: Buffer.alloc(1024) });
}
snapshot(iterasi);
}
console.log('cache size:', cache.length);# jalankan leak-demo.js dengan --expose-gc agar gc() dapat dipanggil eksplisit
# sed menyelaraskan nilai heapUsed yang bervariasi beberapa MB antar jalankan
node --expose-gc leak-demo.js | sed -E 's/heapUsed=[0-9]+MB/heapUsed=NMB/g'iterasi 0: heapUsed=NMB external=2MB arrayBuffers=0MB
iterasi 1: heapUsed=NMB external=99MB arrayBuffers=98MB
iterasi 2: heapUsed=NMB external=197MB arrayBuffers=195MB
iterasi 3: heapUsed=NMB external=295MB arrayBuffers=293MB
iterasi 4: heapUsed=NMB external=392MB arrayBuffers=391MB
iterasi 5: heapUsed=NMB external=490MB arrayBuffers=488MB
cache size: 500000Setiap iterasi menambah 100000 objek ke larik cache, masing-masing memuat Buffer.alloc(1024) sebesar 1 KB, sehingga total alokasi per iterasi sekitar 100 MB. Nilai external dan arrayBuffers tumbuh linear mengikuti jumlah Buffer yang dialokasikan, karena Buffer disimpan di luar heap V8. Nilai heapUsed juga tumbuh, namun lebih lambat karena objek { ts, data } kecil. Karena cache tidak pernah dikosongkan, semua objek tetap direferensikan dan tidak dapat dibebaskan oleh pengumpul sampah.
Pemakaian flag --expose-gc membuka fungsi global.gc() yang memaksa pengumpulan sampah. Pemanggilan gc() sebelum snapshot memastikan hanya objek yang masih direferensikan yang dihitung pada heapUsed, sehingga nilai yang dicetak stabil antar run. Tanpa --expose-gc, heapUsed dapat bervariasi beberapa megabita karena V8 menjadwalkan pengumpulan sampah secara adaptif.
Kebocoran memori#
Kebocoran memori pada Node terjadi ketika objek tetap direferensikan padahal sudah tidak diperlukan, sehingga pengumpul sampah tidak dapat membebaskannya. Penyebab umum meliputi larik global yang tumbuh tanpa batas, penerima peristiwa yang tidak dilepas, serta closure yang menyimpan referensi besar. Pada contoh leak-demo.js di atas, larik cache yang tidak pernah dipangkas adalah kebocoran tipe pertama.
Deteksi kebocoran dilakukan dengan mengamati tren heapUsed selama beberapa menit pada beban yang menyerupai production. Bila heapUsed naik terus tanpa pernah turun setelah beban berhenti, kebocoran terjadi. V8 seharusnya menjadwalkan pengumpulan sampah secara berkala, sehingga heapUsed naik lalu turun membentuk gigi gergaji. Grafik yang hanya naik tanpa turun adalah indikasi kuat adanya kebocoran.
Profiling CPU dan heap#
Node menyediakan tiga flag profiling utama yang menghasilkan berkas jejak yang dapat dianalisis. Flag --cpu-prof merekam profil CPU ke dalam format .cpuprofile yang dapat dibuka di Chrome DevTools. Flag --heap-prof merekam alokasi heap untuk menemukan objek yang dialokasikan paling banyak. Flag --prof menghasilkan jejak V8 dalam format biner isolate-*.log yang diproses dengan perintah --prof-process menjadi laporan teks.
# tidak-dijalankan: hasil profil tidak deterministik, jalankan pada beban nyata
# jalankan aplikasi dengan profil CPU aktif selama 30 detik
node --cpu-prof --cpu-prof-interval=1000 server.js &
# hentikan setelah 30 detik untuk menghentikan rekaman profil
sleep 30
kill -INT $!
# berkas CPU.Cpuprofile-file muncul di direktori kerja saat iniKeluaran --cpu-prof adalah berkas CPU.{YYYYMMDD}_{HHMMSS}.cpuprofile yang memuat cuplikan tumpukan pemanggilan fungsi (sample stack) pada interval tertentu. Berkas ini dimuat di Chrome DevTools melalui tab Performance dengan memilih Load profile, yang kemudian menampilkan flame graph, yaitu grafik tumpukan pemanggilan fungsi berdasarkan waktu CPU yang dihabiskan. Fungsi yang menempati porsi besar pada flame graph adalah kandidat optimasi.
--heap-prof menghasilkan berkas Heap.{timestamp}.heapprofile yang memuat setiap alokasi heap. Berkas ini dimuat di tab Memory > Allocation sampling dan menampilkan fungsi yang mengalokasikan paling banyak objek. Kombinasi --cpu-prof dan --heap-prof memberi gambaran lengkap: yang pertama menemukan fungsi yang lambat, sedangkan yang kedua menemukan fungsi yang boros memori.
Diagram berikut memvisualkan siklus profiling yang berputar dari pengukuran dasar hingga perbaikan dan pengukuran ulang.
Load testing dengan autocannon#
Load testing mengukur seberapa banyak permintaan per detik yang dapat dilayani aplikasi serta latensi pada berbagai tingkat konkurensi. Pustaka autocannon adalah load tester berbasis Node yang dapat diinstal lewat npm dan dijalankan dari CLI atau dipanggil dari skrip. Konfigurasi autocannon dapat ditulis sebagai berkas JSON yang memuat URL, jumlah koneksi konkuren, durasi, dan timeout.
{
"url": "http://127.0.0.1:8080",
"connections": 50,
"duration": 10,
"pipelining": 1,
"timeout": 5,
"renderHeaders": true,
"method": "GET",
"path": "/"
}# tidak-dijalankan: load testing memerlukan server yang berjalan, jalankan di lingkungan production-like
# pasang autocannon di folder terpisah
npm init -y && npm install autocannon
# jalankan autocannon dengan konfigurasi JSON
npx autocannon --config autocannon.jsonKonfigurasi di atas mengarahkan autocannon ke http://127.0.0.1:8080 dengan 50 koneksi konkuren selama 10 detik. Field pipelining mengatur jumlah permintaan yang dikirim per koneksi tanpa menunggu respons, sedangkan timeout membatasi durasi satu permintaan sebelum dianggap gagal. Hasil autocannon mencakup statistik req/sec, latensi rata-rata, serta persentil p99 yang penting untuk menilai ekor distribusi.
Hasil load testing bersifat non-deterministik karena dipengaruhi oleh beban mesin, jaringan, dan konkurensi proses lain. Karena itu, load testing sebaiknya dilakukan pada lingkungan yang menyerupai production dengan beban latar yang minimal, dan dijalankan beberapa kali untuk mengamati variansitas. Perbandingan antar versi aplikasi sebaiknya dilakukan pada mesin yang sama dengan urutan acak untuk mengurangi bias urutan.
Kesalahan umum#
Kesalahan yang sering muncul adalah menyimpulkan kebocoran memori dari kenaikan rss saja. Nilai rss naik ketika V8 mengalokasikan heap baru, tetapi tidak turun segera setelah objek dibebaskan karena V8 menyimpan halaman untuk dipakai ulang. Kenaikan rss yang kemudian stabil menandakan pertumbuhan normal menuju steady state, bukan kebocoran. Indikasi kebocoran yang lebih andal adalah heapUsed yang naik tanpa pernah turun setelah beban berhenti.
Kesalahan lain adalah menjalankan load testing dari mesin yang sama dengan server. CPU yang dibagi antara autocannon dan server membuat hasil tidak merepresentasikan kapasitas sebenarnya. Perbaikannya adalah menjalankan load tester pada mesin terpisah, atau paling tidak membatasi server pada satu inti sementara autocannon memakai inti lain, sehingga pengukuran lebih bersih.
Ringkasan#
process.memoryUsage()mengembalikanrss,heapTotal,heapUsed,external, danarrayBuffersyang dipakai untuk memantau tren memori.externaldanarrayBuffersstabil dan cocok untuk mendeteksi pertumbuhan Buffer.- Kebocoran memori dideteksi dengan mengamati
heapUsedpada beban yang berulang. Grafik yang hanya naik tanpa turun adalah indikasi kebocoran, sedangkan kenaikanrsssaja belum tentu. - Flag
--cpu-profmerekam profil CPU,--heap-profmerekam alokasi heap, dan--profmenghasilkan jejak V8 biner yang diproses dengan--prof-process. Berkas.cpuprofiledan.heapprofiledibuka di Chrome DevTools. autocannonmengukur throughput dan latensi pada berbagai tingkat konkurensi. Konfigurasi JSON menentukan URL, koneksi, durasi, dan timeout. Hasil non-deterministik sehingga perlu beberapa run dan lingkungan yang menyerupai production.
Referensi#
- process.memoryUsage() — Node.js
— Referensi resmi yang merincikan field
rss,heapTotal,heapUsed,external, danarrayBuffers. - CPU profiling — Node.js
— Dokumentasi resmi flag
--cpu-prof,--cpu-prof-interval, dan berkas.cpuprofileyang dihasilkan. - Heap profiling — Node.js
— Dokumentasi resmi flag
--heap-profdan format.heapprofileuntuk analisis alokasi. - autocannon — npm
— Halaman resmi paket
autocannondengan opsi konfigurasi dan contoh pemakaian CLI.