Ini sudah lama menjadi keluhan dengan Java, tetapi sebagian besar tidak ada artinya, dan biasanya didasarkan pada melihat informasi yang salah. Ungkapan yang biasa adalah sesuatu seperti "Halo Dunia di Jawa membutuhkan 10 megabita! Mengapa perlu itu?" Nah, inilah cara untuk membuat Hello World pada JVM 64-bit mengklaim untuk mengambil alih 4 gigabytes ... setidaknya dengan satu bentuk pengukuran.
java -Xms1024m -Xmx4096m com.example.Halo
Cara Berbeda untuk Mengukur Memori
Di Linux, perintah teratas memberi Anda beberapa angka berbeda untuk memori. Inilah yang dikatakan tentang contoh Hello World:
PID PENGGUNA PR NI VIRT RES SHR S% CPU% MEM TIME + PERINTAH
2120 kggregory 20 0 4373m 15m 7152 S 0 0.2 0: 00.10 java
- VIRT adalah ruang memori virtual: jumlah semua yang ada di peta memori virtual (lihat di bawah). Sebagian besar tidak ada artinya, kecuali kalau tidak (lihat di bawah).
- RES adalah ukuran yang ditetapkan residen: jumlah halaman yang saat ini resident dalam RAM. Dalam hampir semua kasus, ini adalah satu-satunya nomor yang harus Anda gunakan ketika mengatakan "terlalu besar." Tapi itu masih bukan angka yang sangat baik, terutama ketika berbicara tentang Jawa.
- SHR adalah jumlah memori penduduk yang dibagi dengan proses lain. Untuk proses Java, ini biasanya terbatas pada perpustakaan bersama dan JARfiles yang dipetakan memori. Dalam contoh ini, saya hanya menjalankan satu proses Java, jadi saya curiga bahwa 7k adalah hasil dari pustaka yang digunakan oleh OS.
- SWAP tidak diaktifkan secara default, dan tidak ditampilkan di sini. Ini menunjukkan jumlah memori virtual yang saat ini ada pada disk, terlepas dari apakah itu benar-benar ada di ruang swap . OS sangat baik untuk menjaga halaman aktif dalam RAM, dan satu-satunya obat untuk bertukar adalah (1) membeli lebih banyak memori, atau (2) mengurangi jumlah proses, jadi yang terbaik adalah mengabaikan nomor ini.
Situasi untuk Windows Task Manager sedikit lebih rumit. Di bawah Windows XP, ada kolom "Penggunaan Memori" dan "Ukuran Memori Virtual", tetapi dokumentasi resmi tidak menjelaskan apa artinya. Windows Vista dan Windows 7 menambahkan lebih banyak kolom, dan sebenarnya didokumentasikan . Dari jumlah tersebut, pengukuran "Perangkat Kerja" adalah yang paling berguna; kira-kira sama dengan jumlah RES dan SHR di Linux.
Memahami Peta Memori Virtual
Memori virtual yang dikonsumsi oleh suatu proses adalah total semua yang ada di peta memori proses. Ini termasuk data (misalnya, tumpukan Java), tetapi juga semua perpustakaan bersama dan file yang dipetakan memori yang digunakan oleh program. Di Linux, Anda dapat menggunakan perintah pmap untuk melihat semua hal yang dipetakan ke dalam ruang proses (mulai sekarang saya hanya akan merujuk ke Linux, karena itulah yang saya gunakan; Saya yakin ada alat yang setara untuk Windows). Berikut ini kutipan dari peta memori program "Hello World"; seluruh peta memori memiliki panjang lebih dari 100 baris, dan bukan tidak biasa memiliki daftar seribu baris.
0000000040000000 36K rx-- /usr/local/java/jdk-1.6-x64/bin/java
0000000040108000 8K rwx-- /usr/local/java/jdk-1.6-x64/bin/java
0000000040eba000 676K rwx-- [anon]
00000006fae00000 21248K rwx-- [anon]
00000006fc2c0000 62720K rwx-- [anon]
0000000700000000 699072K rwx-- [anon]
000000072aab0000 2097152K rwx-- [anon]
00000007aaab0000 349504K rwx-- [anon]
00000007c0000000 1048576K rwx-- [anon]
...
00007fa1ed00d000 1652K r-xs- /usr/local/java/jdk-1.6-x64/jre/lib/rt.jar
...
00007fa1ed1d3000 1024K rwx-- [anon]
00007fa1ed2d3000 4K ----- [anon]
00007fa1ed2d4000 1024K rwx-- [anon]
00007fa1ed3d4000 4K ----- [anon]
...
00007fa1f20d3000 164K rx-- /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
00007fa1f20fc000 1020K ----- /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
00007fa1f21fb000 28K rwx-- /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
...
00007fa1f34aa000 1576K rx-- /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3634000 2044K ----- /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3833000 16K rx-- /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3837000 4K rwx-- /lib/x86_64-linux-gnu/libc-2.13.so
...
Penjelasan cepat format: setiap baris dimulai dengan alamat memori virtual segmen. Ini diikuti oleh ukuran segmen, izin, dan sumber segmen. Item terakhir ini adalah file atau "anon", yang menunjukkan blok memori yang dialokasikan melalui mmap .
Mulai dari atas, sudah
- Pemuat JVM (yaitu, program yang dijalankan saat Anda mengetik
java
). Ini sangat kecil; semua yang dilakukannya adalah memuat di pustaka bersama tempat kode JVM asli disimpan.
- Sekelompok blok anon memegang heap Java dan data internal. Ini adalah Sun JVM, jadi tumpukannya dibagi menjadi beberapa generasi, yang masing-masingnya merupakan blok memori sendiri. Perhatikan bahwa JVM mengalokasikan ruang memori virtual berdasarkan
-Xmx
nilai; ini memungkinkannya untuk memiliki tumpukan yang berdekatan. The -Xms
nilai digunakan secara internal untuk mengatakan berapa banyak tumpukan adalah "digunakan" ketika program dimulai, dan untuk pengumpulan sampah pemicu batas yang didekati.
- JARfile yang dipetakan dengan memori, dalam hal ini file yang menampung "kelas JDK." Ketika Anda memetakan memori sebuah JAR, Anda dapat mengakses file di dalamnya dengan sangat efisien (dibandingkan membacanya dari awal setiap kali). Sun JVM akan memetakan-memori semua JAR di classpath; jika kode aplikasi Anda perlu mengakses JAR, Anda juga dapat memetakannya dalam memori.
- Data per-utas untuk dua utas. Blok 1M adalah tumpukan ulir. Saya tidak memiliki penjelasan yang baik untuk blok 4k, tetapi @ericsoe mengidentifikasinya sebagai "blok penjaga": ia tidak memiliki izin baca / tulis, sehingga akan menyebabkan kesalahan segmen jika diakses, dan JVM menangkap itu dan menerjemahkan itu ke
StackOverFlowError
. Untuk aplikasi nyata, Anda akan melihat puluhan jika tidak ratusan entri ini diulang melalui peta memori.
- Salah satu pustaka bersama yang menyimpan kode JVM yang sebenarnya. Ada beberapa di antaranya.
- Pustaka bersama untuk pustaka standar C. Ini hanyalah salah satu dari banyak hal yang dimuat JVM yang tidak sepenuhnya bagian dari Jawa.
Pustaka bersama sangat menarik: setiap pustaka bersama memiliki setidaknya dua segmen: segmen hanya baca yang berisi kode perpustakaan, dan segmen baca-tulis yang berisi data per proses global untuk perpustakaan (saya tidak tahu apa segmen tanpa izin adalah; Saya hanya melihatnya di x64 Linux). Bagian read-only perpustakaan dapat dibagi antara semua proses yang menggunakan perpustakaan; misalnya, libc
memiliki ruang memori virtual 1,5M yang dapat dibagi.
Kapan Ukuran Memori Virtual Penting?
Peta memori virtual berisi banyak hal. Beberapa di antaranya hanya-baca, sebagian dibagikan, dan sebagian dialokasikan tetapi tidak pernah disentuh (misalnya, hampir semua tumpukan 4Gb dalam contoh ini). Tetapi sistem operasi cukup pintar untuk hanya memuat apa yang dibutuhkan, sehingga ukuran memori virtual sebagian besar tidak relevan.
Di mana ukuran memori virtual penting adalah jika Anda menjalankan sistem operasi 32-bit, di mana Anda hanya dapat mengalokasikan 2Gb (atau, dalam beberapa kasus, 3Gb) ruang alamat proses. Dalam hal ini Anda berhadapan dengan sumber daya yang langka, dan mungkin harus melakukan pengorbanan, seperti mengurangi ukuran tumpukan Anda untuk memetakan memori file besar atau membuat banyak utas.
Tetapi, mengingat bahwa mesin 64-bit ada di mana-mana, saya pikir itu tidak akan lama sebelum Ukuran Memori Virtual adalah statistik yang sama sekali tidak relevan.
Kapan Resident Mengatur Ukuran Penting?
Ukuran Resident Set adalah porsi ruang memori virtual yang sebenarnya ada dalam RAM. Jika RSS Anda tumbuh menjadi bagian yang signifikan dari total memori fisik Anda, mungkin ini saatnya untuk mulai khawatir. Jika RSS Anda tumbuh untuk mengambil semua memori fisik Anda, dan sistem Anda mulai bertukar, sudah lewat waktu untuk mulai khawatir.
Tapi RSS juga menyesatkan, terutama pada mesin yang ringan. Sistem operasi tidak menghabiskan banyak upaya untuk merebut kembali halaman yang digunakan oleh suatu proses. Ada sedikit manfaat yang bisa diperoleh dengan melakukannya, dan potensi kesalahan halaman yang mahal jika proses menyentuh halaman di masa depan. Akibatnya, statistik RSS dapat menyertakan banyak halaman yang tidak digunakan secara aktif.
Intinya
Kecuali jika Anda bertukar, jangan terlalu khawatir tentang apa yang dikatakan berbagai statistik memori. Dengan peringatan bahwa RSS yang terus tumbuh dapat mengindikasikan semacam kebocoran memori.
Dengan program Java, jauh lebih penting untuk memperhatikan apa yang terjadi di heap. Jumlah total ruang yang digunakan adalah penting, dan ada beberapa langkah yang dapat Anda ambil untuk menguranginya. Yang lebih penting adalah jumlah waktu yang Anda habiskan dalam pengumpulan sampah, dan bagian mana dari tumpukan yang dikumpulkan.
Mengakses disk (yaitu, database) mahal, dan memori murah. Jika Anda dapat berdagang satu untuk yang lain, lakukanlah.