Apa yang mengkonsumsi memori dalam proses java?

20

kami mencoba untuk menyelidiki penggunaan memori proses java di bawah beban sedang.

  PID   USER    PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
  12663 test    20   0 8378m 6.0g 4492 S   43  8.4 162:29.95 java

Seperti yang Anda lihat kami memiliki memori penduduk di 6Gb. Sekarang bagian yang menarik adalah ini: proses dijalankan dengan params ini:

  • -Xmx2048m
  • -Xms2048m
  • -XX: NewSize = 512m
  • -XX: MaxDirectMemorySize = 256m
  • ... beberapa lainnya untuk GC dan lainnya

Melihat pengaturan ini dan penggunaan memori yang sebenarnya, kami tersandung untuk melihat perbedaan dari apa yang kami harapkan akan digunakan oleh proses ini dan apa yang sebenarnya digunakan.

Biasanya masalah ingatan kita diselesaikan dengan menganalisis heap dump tetapi dalam kasus ini ingatan kita digunakan di suatu tempat di luar heap.

Pertanyaan: Apa langkah-langkah untuk mencoba dan menemukan alasan penggunaan memori yang sedemikian tinggi? Alat apa yang dapat membantu kami mengidentifikasi apa yang menggunakan memori dalam proses itu?

EDIT 0

Sepertinya ini bukan masalah yang berhubungan dengan tumpukan karena kita masih memiliki beberapa ruang di sana:

jmap -heap 12663

hasil dalam (diedit untuk menghemat ruang)

Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize      = 2147483648 (2048.0MB)
NewSize          = 536870912 (512.0MB)
MaxNewSize       = 536870912 (512.0MB)
OldSize          = 1610612736 (1536.0MB)
NewRatio         = 7
SurvivorRatio    = 8
PermSize         = 21757952 (20.75MB)
MaxPermSize      = 85983232 (82.0MB)

New Generation: 45.7% used
Eden Space: 46.3% used
From Space: 41.4% used
To Space: 0.0% used
concurrent mark-sweep generation: 63.7% used
Perm Generation: 82.5% used

EDIT 1

menggunakan pmap kita dapat melihat bahwa ada sejumlah alokasi 64Mb:

pmap -x 12663 | grep rwx | sort -n -k3 | less

menghasilkan:

... a lot more of these 64Mb chunks
00007f32b8000000       0   65508   65508 rwx--    [ anon ] <- what are these?
00007f32ac000000       0   65512   65512 rwx--    [ anon ]
00007f3268000000       0   65516   65516 rwx--    [ anon ]
00007f3324000000       0   65516   65516 rwx--    [ anon ]
00007f32c0000000       0   65520   65520 rwx--    [ anon ]
00007f3314000000       0   65528   65528 rwx--    [ anon ] 
00000000401cf000       0  241904  240980 rwx--    [ anon ] <- Direct memory ?
000000077ae00000       0 2139688 2139048 rwx--    [ anon ] <- Heap ?

Jadi bagaimana cara mengetahui potongan 64Mb itu? Apa yang menggunakannya? Jenis data apa yang ada di dalamnya?

Terima kasih

Konstantin S.
sumber
2
Saya mendapatkan masalah yang persis sama ... ini pertanyaan saya. stackoverflow.com/questions/18734389/… Apakah Anda punya solusi tentang ini?
DeepNightTwo

Jawaban:

21

Masalahnya mungkin terkait dengan masalah glibc ini .

Pada dasarnya, ketika Anda memiliki banyak utas yang mengalokasikan memori, glibc akan meningkatkan jumlah arena yang tersedia untuk melakukan alokasi dari untuk menghindari pertikaian kunci. Arena berukuran 64 MB. Batas atas adalah membuat 8 kali jumlah arena inti. Arena akan dibuat sesuai permintaan ketika utas mengakses arena yang sudah dikunci sehingga berkembang seiring waktu.

Di Jawa tempat Anda menaburi utas, ini dapat dengan cepat menyebabkan banyak arena dibuat. Dan ada alokasi yang tersebar di seluruh arena ini. Awalnya setiap arena 64Mb hanya memetakan memori yang tidak terhubung, tetapi saat Anda mengalokasikan Anda mulai menggunakan memori aktual untuknya.

Pmap Anda mungkin memiliki daftar yang mirip dengan di bawah ini. Perhatikan bagaimana 324K + 65212K = 65536K, 560K + 64976K == 65536K, 620K + 64916K == 65536K. Artinya, jumlah mereka hingga 64Mb.

00007f4394000000 324K rw --- [anon]
00007f4394051000 65212K ----- [anon]
00007f4398000000 560K rw --- [anon]
00007f439808c000 64976K ----- [anon]
00007f439c000000 620K rw --- [anon]
00007f439c09b000 64916K ----- [anon]

Adapun solusinya : Bug menyebutkan beberapa parameter lingkungan yang dapat Anda atur untuk membatasi jumlah arena, tetapi Anda memerlukan versi glibc yang cukup tinggi.

Kristen
sumber
5
pengaturan Xmx dan Xms ke nilai yang sama, ditambah pengaturan variabel lingkungan "export MALLOC_ARENA_MAX = 4" dalam skrip sh yang memulai layanan web kami membantu dalam kasus kami. Sebelumnya kami mengalami restart layanan web karena OOM Killer setiap 2 hingga 8 jam. Versi GLIBC di Ubuntu 14.04 adalah 2.19 yang baik, karena harus> = 2.16 agar pengaturan MALLOC_ARENA_MAX berfungsi
Kluyg
Jawaban ini dan komentar di atas adalah penyelamat bagi saya. Dalam kasus saya, MALLOC_ARENA_MAX = 1 diperlukan dan efektif.
John Bachir 2-15
3

Bagaimana dengan Probe Lamdba ? Di antara hal-hal lain itu dapat menunjukkan kerusakan penggunaan memori yang mirip dengan tangkapan layar di bawah ini:

Tampilan penggunaan memori Lambda Probe

Terkadang pmap -x your_java_pidjuga bisa membantu.

Janne Pikkarainen
sumber
Terima kasih atas jawaban anda. Jika saya mengerti dengan benar maka Lambda Probe untuk Apache Tomcat? Yang tidak kami gunakan ... Mengenai pmap saya akan menambahkan info ke posting teratas
Konstantin S.
2

JProfiler bisa menjadi sesuatu yang Anda cari tetapi tidak gratis. Alat lain yang bagus dan gratis untuk menyelidiki penggunaan memori proses java adalah Java VisualVM yang tersedia sebagai alat JDK dalam distribusi Oracle / Sun JDK. Saya pribadi merekomendasikan pendekatan yang lebih holistik untuk masalah ini (yaitu untuk memonitor disk JDK + OS +, dll) - penggunaan beberapa sistem pemantauan jaringan - Nagios, Verax NMS atau OpenNMS.

xantross
sumber
2
Kebocorannya tidak terkendali sehingga JProfiler tidak akan membantu di sini
Asaf Mesika
2

Masalahnya adalah di luar tumpukan, jadi kandidat terbaik adalah:

JNI leak  
Allocation of direct memory buffer

Karena Anda telah membatasi ukuran buffer langsung, kandidat terbaik menurut saya adalah kebocoran JNI.

Venceslas
sumber
1

Ada alat yang berguna untuk melihat alokasi memori heap termasuk dalam JDK disebut jmap, di atas ini Anda juga memiliki tumpukan dll (Xss). Jalankan kedua perintah jmap ini untuk mendapatkan informasi lebih lanjut tentang penggunaan memori:

jmap -heap <PID>
jmap -permstat <PID>

Untuk mendapatkan lebih banyak informasi lebih lanjut Anda dapat terhubung ke proses dengan jconsole (juga termasuk dalam JDK). Namun Jconsole memerlukan JMX untuk dikonfigurasikan dalam aplikasi.

HampusLi
sumber
Terima kasih atas jawaban Anda tetapi masalahnya tampaknya berada di suatu tempat di luar tumpukan. Saya akan memperbarui posting teratas untuk mencerminkan beberapa informasi dari jmap
Konstantin S.
Berapa banyak utas yang dimiliki proses ini? Ukuran tumpukan adalah 2MB default pada kebanyakan platform, jadi gandakan dengan jumlah utas. Saya akan terkejut jika itu menyumbang semua "hilang" memori tetapi mungkin beberapa di antaranya.
HampusLi
Sekitar 300 utas, informasi yang diambil dari pmap kami memiliki ukuran tumpukan 1 MB. Dan dari output pmap yang sama tidak terlihat seperti tumpukan yang menggunakan lebih dari 100Kb
Konstantin S.
0

Gunakan JVisualVM. Ini memiliki berbagai pandangan yang berbeda yang akan memberi tahu Anda berapa banyak memori tumpukan yang digunakan, PermGen dan seterusnya.

Adapun untuk menjawab pertanyaan Anda. Java menangani memori dengan sangat berbeda dari yang Anda harapkan.

Ketika Anda mengatur parameter -Xms dan -Xmx, Anda memberi tahu JVM berapa banyak memori yang harus dialokasikan di heap untuk memulainya dan juga berapa banyak yang seharusnya dialokasikan sebagai maksimum.

Jika Anda memiliki aplikasi java yang menggunakan total memori 1m tetapi melewati -Xms256m -Xmx2g maka JVM akan menginisialisasi dirinya dengan 256m memori yang digunakan. Itu tidak akan menggunakan kurang dari itu. Tidak masalah bahwa aplikasi Anda hanya menggunakan memori 1m.

Kedua. Dalam kasus di atas, jika aplikasi Anda pada titik tertentu menggunakan lebih dari 256m memori, JVM akan mengalokasikan memori sebanyak yang diperlukan untuk melayani permintaan. Namun, itu tidak akan menurunkan ukuran tumpukan kembali ke nilai minimum. Setidaknya, tidak dalam sebagian besar keadaan.

Dalam kasus Anda, karena Anda menetapkan memori minimum dan maksimum pada 2g, JVM akan mengalokasikan 2g di awal dan memeliharanya.

Manajemen memori Java cukup rumit dan menyetel penggunaan memori dapat menjadi tugas tersendiri. Namun, ada banyak sumber daya di luar sana yang dapat membantu.

drone.ah
sumber