Saya punya masalah dengan aplikasi Java yang berjalan di Linux.
Ketika saya meluncurkan aplikasi, menggunakan ukuran tumpukan maksimum default (64 MB), saya melihat menggunakan aplikasi puncak yang 240 MB Memori virtual dialokasikan ke aplikasi. Ini menciptakan beberapa masalah dengan beberapa perangkat lunak lain di komputer, yang relatif terbatas sumber daya.
Memori virtual yang dicadangkan tidak akan digunakan, sejauh yang saya mengerti, karena begitu kita mencapai batas tumpukan dan OutOfMemoryError
dilemparkan. Saya menjalankan aplikasi yang sama di bawah windows dan saya melihat bahwa ukuran Memori Virtual dan ukuran Heap serupa.
Apakah ada cara lain yang dapat saya atur Memori Virtual yang digunakan untuk proses Java di Linux?
Sunting 1 : Masalahnya bukan Heap. Masalahnya adalah jika saya menetapkan Heap 128 MB, misalnya, Linux masih mengalokasikan 210 MB Memori Virtual, yang tidak diperlukan, selamanya. **
Sunting 2 : Menggunakan ulimit -v
memungkinkan membatasi jumlah memori virtual. Jika ukuran yang ditetapkan di bawah 204 MB, maka aplikasi tidak akan berjalan meskipun tidak perlu 204 MB, hanya 64 MB. Jadi saya ingin mengerti mengapa Java membutuhkan begitu banyak memori virtual. Bisakah ini diubah?
Sunting 3 : Ada beberapa aplikasi lain yang berjalan di sistem, yang tertanam. Dan sistem memang memiliki batas memori virtual (dari komentar, detail penting).
sumber
Jawaban:
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.
Cara Berbeda untuk Mengukur Memori
Di Linux, perintah teratas memberi Anda beberapa angka berbeda untuk memori. Inilah yang dikatakan tentang contoh Hello World:
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.
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
java
). Ini sangat kecil; semua yang dilakukannya adalah memuat di pustaka bersama tempat kode JVM asli disimpan.-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.StackOverFlowError
. Untuk aplikasi nyata, Anda akan melihat puluhan jika tidak ratusan entri ini diulang melalui peta memori.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.
sumber
Ada masalah yang diketahui dengan Java dan glibc> = 2.10 (termasuk Ubuntu> = 10.04, RHEL> = 6).
Obatnya adalah mengatur env ini. variabel:
Jika Anda menjalankan Tomcat, Anda dapat menambahkan ini ke
TOMCAT_HOME/bin/setenv.sh
file.Untuk Docker, tambahkan ini ke Dockerfile
Ada artikel IBM tentang pengaturan MALLOC_ARENA_MAX https://www.ibm.com/developerworks/community/blogs/kevgrig/entry/linux_glibc_2_10_rhel_6_malloc_may_show_excessive_virtual_memory_usage?lang=en
Posting blog ini mengatakan
Ada juga bug JDK terbuka JDK-8193521 "glibc menghabiskan memori dengan konfigurasi default"
cari MALLOC_ARENA_MAX di Google atau SO untuk referensi lainnya.
Anda mungkin ingin menyetel juga opsi malloc lainnya untuk dioptimalkan agar fragmentasi rendah dari memori yang dialokasikan:
sumber
MALLOC_ARENA_MAX
mungkin memperlambat pertumbuhan memori Anda, tetapi tidak menyelesaikan masalah sepenuhnya.Jumlah memori yang dialokasikan untuk proses Java cukup setara dengan apa yang saya harapkan. Saya punya masalah serupa menjalankan Java pada embedded / memory system terbatas. Menjalankan aplikasi apa pun dengan batas VM sewenang-wenang atau pada sistem yang tidak memiliki jumlah swap yang memadai cenderung rusak. Tampaknya menjadi sifat dari banyak aplikasi modern yang tidak dirancang untuk digunakan pada sistem terbatas sumber daya.
Anda memiliki beberapa opsi lagi yang dapat Anda coba dan batasi jejak memori JVM Anda. Ini mungkin mengurangi jejak memori virtual:
Selain itu, Anda juga harus mengatur -Xmx (ukuran tumpukan maksimum) ke nilai sedekat mungkin dengan penggunaan memori puncak aktual aplikasi Anda. Saya percaya perilaku default JVM masih dua kali lipat ukuran heap setiap kali itu memperluas hingga max. Jika Anda mulai dengan tumpukan 32 juta dan aplikasi Anda memuncak menjadi 65 juta, maka tumpukan akan berakhir dengan pertumbuhan 32 juta -> 64 juta -> 128 juta.
Anda mungkin juga mencoba ini untuk membuat VM kurang agresif tentang menumbuhkan tumpukan:
Juga, dari apa yang saya ingat dari percobaan dengan ini beberapa tahun yang lalu, jumlah perpustakaan asli dimuat memiliki dampak besar pada jejak minimum. Memuat java.net.Socket menambahkan lebih dari 15 juta jika saya mengingat dengan benar (dan saya mungkin tidak).
sumber
Sun JVM membutuhkan banyak memori untuk HotSpot dan memetakan di pustaka runtime dalam memori bersama.
Jika memori menjadi masalah, pertimbangkan untuk menggunakan JVM lain yang cocok untuk disematkan. IBM memiliki j9, dan ada Open Source "jamvm" yang menggunakan perpustakaan classpath GNU. Sun juga memiliki Squeak JVM yang berjalan di SunSPOT jadi ada alternatif lain.
sumber
Hanya berpikir, tetapi Anda dapat memeriksa pengaruh sebuah
ulimit -v
pilihan .Itu bukan solusi aktual karena akan membatasi ruang alamat yang tersedia untuk semua proses, tetapi itu akan memungkinkan Anda untuk memeriksa perilaku aplikasi Anda dengan memori virtual terbatas.
sumber
Salah satu cara mengurangi tumpukan heile dari suatu sistem dengan sumber daya terbatas mungkin dengan bermain-main dengan variabel -XX: MaxHeapFreeRatio. Ini biasanya diatur ke 70, dan persentase maksimum tumpukan yang gratis sebelum GC menyusut. Menetapkannya ke nilai yang lebih rendah, dan Anda akan melihat misalnya profiler jvisualvm bahwa sice heap yang lebih kecil biasanya digunakan untuk program Anda.
Sunting: Untuk menetapkan nilai kecil untuk -XX: MaxHeapFreeRatio Anda juga harus mengatur -XX: MinHeapFreeRatio Misalnya
EDIT2: Menambahkan contoh untuk aplikasi nyata yang memulai dan melakukan tugas yang sama, satu dengan parameter default dan satu dengan 10 dan 25 sebagai parameter. Saya tidak melihat adanya perbedaan kecepatan nyata, meskipun java secara teori harus menggunakan lebih banyak waktu untuk meningkatkan heap pada contoh terakhir.
Pada akhirnya, max heap adalah 905, heap yang digunakan adalah 378
Pada akhirnya, max heap adalah 722, heap yang digunakan adalah 378
Ini sebenarnya memiliki beberapa inpact, karena aplikasi kita berjalan pada server desktop jarak jauh, dan banyak pengguna dapat menjalankannya sekaligus.
sumber
Sun's Java 1.4 memiliki argumen berikut untuk mengontrol ukuran memori:
http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/java.html
Java 5 dan 6 memiliki lebih banyak. Lihat http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp
sumber
Tidak, Anda tidak dapat mengonfigurasi jumlah memori yang dibutuhkan oleh VM. Namun, perhatikan bahwa ini adalah memori virtual, bukan residen, jadi itu hanya tinggal di sana tanpa membahayakan jika tidak benar-benar digunakan.
Secara alternatif, Anda dapat mencoba beberapa JVM lain kemudian Sun satu, dengan jejak memori yang lebih kecil, tapi saya tidak bisa menyarankan di sini.
sumber