Java-buildpack: Menyetel Variabel Lingkungan Glibc (misalnya MALLOC_ARENA_MAX)

Dibuat pada 12 Agu 2016  ·  12Komentar  ·  Sumber: cloudfoundry/java-buildpack

Ini adalah masalah lain yang kami diskusikan @nebhale.

Setelah melihat beberapa masalah memori Java baru-baru ini, saya menemukan dua artikel berikut dari Heroku (haruskah saya menyebutnya "Yang tidak dapat disebutkan namanya"? : P):

Menyetel Perilaku Memori glibc
Menguji Penggunaan Memori Cedar-14

Yang menunjukkan bahwa menyetel MALLOC_ARENA_MAX dapat membantu masalah memori dalam aplikasi. Tampaknya itu juga menyebabkan masalah bagi Hadoop , meskipun itu mungkin kesalahan mereka sendiri untuk memantau VMEM (serius, kawan, mengapa?!).

Dalam pengujian saya sendiri, saya perhatikan bahwa pengaturan MALLOC_ARENA_MAX tampaknya membantu memperlambat pertumbuhan memori, meskipun _tidak_ mencegahnya sepenuhnya.

Ada implikasi kinerja dari pengaturan MALLOC_ARENA_MAX (Saya selalu menyetelnya ke 2 , seperti yang disarankan oleh Heroku), tetapi hasilnya seperti ini:

  • Utas baru dibuat, meminta arena
  • Jika < MALLOC_ARENA_MAX blok sedang digunakan (default pada 8 * cpu_count ), dapatkan satu.
  • Jika >, bagikan yang sudah ada
  • Ketika malloc ing memori baru, jika memiliki penggunaan eksklusif arena, tidak perlu mengunci.
  • Jika dibagikan, harus dikunci, sehingga mengakibatkan perebutan memori. Sebelum glibc memperkenalkan arena, semua utas bersaing untuk memori yang sama.
  • Saat utas keluar, glibc menandai arena sebagai "gratis", tetapi _tidak_ mengembalikannya ke OS.

Itu pemahaman saya dalam hal apapun. Jadi saya pikir sebagian besar aplikasi Java di CF pada dasarnya tidak terpengaruh karena alasan berikut:

  • Jumlah terbatas dari thread-thrashing. Jika menjadi perhatian utama, dapat mengonfigurasi wadah untuk memiliki min_threads = max_threads . Sebagian besar utas berumur panjang dan memproses banyak permintaan.
  • Java tidak melakukan malloc pada basis per-utas - itu mallocs ruang tumpukan saat memulai, dan kemudian menulisnya sesuai kebutuhan. Sejauh yang saya tahu, setelah utas berjalan, seharusnya tidak perlu melakukan level OS malloc , artinya kami tidak akan pernah memiliki pertikaian level OS untuk memori (yang ditarik ke runtime Java ).

    • NIO dan hal-hal seperti ByteBuffer s mungkin menghasilkan panggilan malloc - tidak yakin tentang itu.

Dengan semua itu, setelah menggali lebih dalam tentang ini, sepertinya pengaturan MALLOC_ARENA_MAX mungkin baru saja menyembunyikan masalah yang lebih dalam, dan saya tidak yakin apakah saya akan menyarankan pengaturannya secara default. Tampaknya tidak mempengaruhi kinerja secara negatif, tetapi kedua aplikasi yang saya uji secara efektif tidak berkinerja baik berdasarkan desain, jadi jangan anggap itu sebagai Injil.

Saya awalnya berpikir bahwa mungkin ada masalah memori di sini, tapi saya tidak begitu yakin lagi. Saya pikir skenario berikut adalah apa yang akan terjadi jika ada kebocoran memori:

MALLOC_ARENAS_MAX tidak terbatas

  • Thread meminta arena
  • Benang mendapat arena
  • Benang memenuhi arena, tidak pernah membebaskan memori
  • Thread meminta arena baru
  • Ulangi sampai cgroup OOM killer nya.

MALLOC_ARENAS_MAX disetel ke 2

  • Thread meminta arena
  • Benang mendapat arena
  • Benang memenuhi arena, tidak pernah membebaskan memori
  • Thread meminta arena baru, tetapi tidak ada lagi yang tersedia.
  • Thread malah menulis ke arena "utama" (atau tumpukan program asli dalam kasus ini), yang tidak terbatas. Diberi waktu yang cukup, cgroup OOM killer hits.

(Keputusan alokasi sebenarnya jauh lebih kompleks - ini sangat disederhanakan. Lihat Memahami glibc malloc untuk penjelasan mendalam).

Ini akan menjelaskan mengapa saya melihat ruang heap yang dilaporkan oleh pmap (yang _bukan_ Java Heap!) naik dan naik di salah satu pengujian saya - ada kebocoran memori di suatu tempat dalam kode asli, arena mendapat terisi, dan setelah penuh, itu harus kembali menggunakan tumpukan asli.

Dengan semua itu, beberapa kelonggaran untuk memori asli yang digunakan oleh masing-masing utas mungkin harus dibuat. Jika cukup kecil, mungkin bisa tetap berada di bagian "asli" kalkulator. Jika tidak, karena relatif terhadap jumlah utas yang digunakan, mungkin perlu dihitung entah bagaimana.

Terlepas dari keputusan untuk mengaturnya, saya _do_ berpikir bahwa mendokumentasikannya akan sangat berguna (bersama dengan bagaimana kaitannya dengan Java, dll), seperti menyediakan tautan ke variabel lingkungan konfigurasi glibc lainnya. Menemukan informasi ini _tidak_ semudah yang saya inginkan. Saya pikir ini juga harus didokumentasikan untuk buildpack lain juga - Ruby dan Go, khususnya, lebih cenderung melihat masalah dengan arena memori - perhatikan bahwa contoh Heroku semuanya berbicara tentang Ruby.

Komentar yang paling membantu

Hai, yang di sana,

Saya perhatikan bahwa TieredCompilation tampaknya menghasilkan pertumbuhan yang berkelanjutan. Menonaktifkan TieredCompilation (-XX:-TieredCompilation), telah menghentikan pertumbuhan ini terjadi pada beberapa aplikasi kami (semua menggunakan jdk8). Saya mengirimkan bug ke Oracle hari ini untuk dievaluasi, yang mungkin terkait dengan bug yang telah disebutkan. http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8164293

Hanya berpikir saya akan menyebutkannya, kalau-kalau ini membantu Anda.

/dom

Semua 12 komentar

@ipsi Apakah Anda mengetahui #160 dan #163 ?

Saya baru saja menerima tweet dari @TimGerlach dari SAP dan dia tampaknya juga tertarik dengan peningkatan di bidang ini. @TimGerlach , Bisakah Anda menguraikan kebutuhan Anda?

Ini akan menjelaskan mengapa saya melihat ruang heap yang dilaporkan oleh pmap (yang bukan Java Heap!) naik dan turun di salah satu pengujian saya - ada kebocoran memori di suatu tempat dalam kode asli, arena terisi, dan sekali mereka penuh, itu harus kembali menggunakan tumpukan asli.

Sudahkah Anda memeriksa artikel yang ditulis oleh Evan Jones ini :

TL;DR: Selalu tutup GZIPInputStream dan GZIPOutputStream karena mereka menggunakan memori asli melalui zlib. Untuk melacak kebocoran, gunakan jemalloc dan aktifkan pembuatan profil pengambilan sampel menggunakan variabel lingkungan MALLOC_CONF.

Baru-baru ini Heroku membuat blog tentang melacak bug serupa di Kafka . Artikel tim GDS GOV UK tentang men- debug kebocoran memori asli juga dapat membantu.

Evan Jones juga menulis blog tentang kebocoran memori asli di Java's ByteBuffer .

Terima kasih @lhotari telah menyebutkan dan mengarahkan saya ke Masalah ini. Saya akan menguraikan #319 karena ini lebih dekat dengan topik kita.

@lhotari Saya mengetahui yang pertama - tidak mengetahui masalah kedua. Sejauh yang saya tahu, sebenarnya ada bug HotSpot, dan ini mungkin yang Anda lihat juga: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8164293

Saya melihat hal GZIP, dan bahkan menjalankan aplikasi saya dengan JEMalloc, tetapi tidak melihat masalah yang sama yang mereka laporkan. Mengingat bahwa masalah ini (a) hanya muncul di Java 8, dan (b) menghilang ketika HotSpot dinonaktifkan, saya menduga ada beberapa masalah yang dapat menyebabkan aplikasi bertambah dalam memori dari waktu ke waktu.

@ipsi Terima kasih telah menunjukkan bug JDK.

Apakah Anda melihat asumsi yang saya sajikan di https://github.com/cloudfoundry/Java-buildpack/issues/163#issue -60842702 ?
Satu asumsi adalah bahwa menyetel CodeCacheExpansionSize dan MinMetaspaceExpansion juga akan mengurangi fragmentasi memori malloc.
Harus ada beberapa faktor yang menyebabkan gejala yang sama dari lambatnya pertumbuhan proses RSS. Itulah alasan utama untuk mengajukan #163 .

Hai, yang di sana,

Saya perhatikan bahwa TieredCompilation tampaknya menghasilkan pertumbuhan yang berkelanjutan. Menonaktifkan TieredCompilation (-XX:-TieredCompilation), telah menghentikan pertumbuhan ini terjadi pada beberapa aplikasi kami (semua menggunakan jdk8). Saya mengirimkan bug ke Oracle hari ini untuk dievaluasi, yang mungkin terkait dengan bug yang telah disebutkan. http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8164293

Hanya berpikir saya akan menyebutkannya, kalau-kalau ini membantu Anda.

/dom

FYI, bug yang saya kemukakan telah ditandai sebagai terselesaikan di https://bugs.openjdk.java.net/browse/JDK-8164293 , versi target adalah 8u152, yang memiliki tanggal rilis yang diharapkan dari 2017-10-16.

Yah, itu kencan yang lucu.

FYI, bug telah di-backport ke 8u131 yang dirilis: https://bugs.openjdk.java.net/browse/JDK-8178124

Nah itu kabar baik. Saya akan membiarkan ini terbuka selama beberapa minggu untuk memungkinkan orang menguji terhadap v3.16 dan v4.0 dan memberi tahu saya jika masih ada masalah yang belum terselesaikan.

Tidak ada keluhan, jadi saya menutup ini.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat