Saat menemukan masalah kinerja JVM, Anda mungkin mengalami kebocoran memori dan menyebabkan JVM outofmemory. Jika parameter reloadable = "true" diatur saat menggunakan wadah Tomcat, Anda juga dapat menemukan overflow memori ketika sering menggunakan aplikasi. Prinsip penyebaran panas Tomcat adalah untuk mendeteksi bahwa file-file di Web-INF/kelas atau direktori Web-INF/LIB diubah dan aplikasi akan dihentikan terlebih dahulu dan kemudian dimulai. Karena Tomcat menetapkan WebAppClassLoader ke setiap aplikasi secara default, prinsip penggantian panas adalah membuat ClassLoader baru untuk memuat kelas. Karena keunikan kelas dalam JVM ditentukan oleh file kelasnya dan loader kelasnya, memuat ulang kelas dapat mencapai tujuan penggantian panas. Ketika jumlah penyebaran panas lebih sering, itu akan menyebabkan lebih banyak kelas yang dimuat oleh JVM. Jika kelas sebelumnya tidak diturunkan pada waktunya karena beberapa alasan (seperti kebocoran memori), itu dapat menyebabkan generasi permanen atau metaspace outofmemory. Artikel ini secara singkat memperkenalkan skenario di mana Threadlocal dan ClassLoader menyebabkan kebocoran memori dan pada akhirnya outofmemory melalui demo.
Menghapus pemasangan kelas
Setelah kelas digunakan, jika situasi berikut dipenuhi, itu akan dihapus:
1. Semua contoh kelas ini di tumpukan telah didaur ulang, yaitu, objek contoh kelas ini tidak ada di tumpukan.
2. ClassLoader memuat kelas ini telah didaur ulang.
3. Objek kelas yang sesuai dengan kelas ini tidak dapat dirujuk di mana pun, dan objek kelas tidak dapat diakses melalui refleksi.
Jika kelas memenuhi kondisi uninstallasi, JVM menghapus instalasi kelas ketika berada di GC, yaitu, menghapus informasi kelas di area metode.
Pendahuluan Adegan
Dalam artikel sebelumnya, saya memperkenalkan prinsip Threadlocal. Setiap utas memiliki threadlocalmap. Jika siklus hidup utas relatif panjang, entri dalam threadlocalmap mungkin tidak didaur ulang. Objek Threadlocal selalu dirujuk dengan kuat oleh utas. Karena objek instan akan memegang referensi objek kelas, objek kelas akan memegang referensi classloader yang memuatnya, yang akan menyebabkan kelas diturunkan. Ketika ada cukup kelas yang dimuat, generasi permanen atau overflow memori Metaspace dapat terjadi. Jika kelas memiliki objek besar, seperti array byte yang lebih besar, itu akan menyebabkan overflow memori area tumpukan java.
Pendahuluan Kode Sumber
Berikut ini adalah bagian dalam kelas internal. Kelas dalam memiliki objek threadlocal statis, yang terutama digunakan untuk membuat utas memegang referensi yang kuat ke kelas dalam, sehingga kelas dalam tidak dapat didaur ulang. Loader kelas khusus didefinisikan untuk memuat kelas dalam, seperti yang ditunjukkan di bawah ini:
public class MemoryLeak { public static void main(String[] args) { //Because the thread is running all the time, the Inner object in ThreadLocalMap has been strongly referenced by Thread object new Thread(new Runnable() { @Override public void run() { while (true) { //Every time a new ClassLoader instance is created to load the Inner class CustomClassLoader classLoader = new CustomClassLoader ("Load1", memoros.class.getClassLoader (), "com.ezlippi.memoryleak $ inner", "com.ezlippi.memoryleak $ inner $ 1"); GC untuk pemrosesan referensi InnerClass = NULL; } // Untuk mencapai area tumpukan kelas statis public lebih cepat {private byte [] mb = byte baru [1024 * 1024]; threadlocal statis <intner> threadlocal = threadlocal baru <intner> () {@Override dilindungi inci initialValue () {return new Inner (); }}; // untuk memanggil threadlocal.get () untuk menginisialisasi objek dalam statis {threadlocal.get (); } public inner () {}} // Sumber Code Omit Private Static Class CustomClassLoader Extends ClassLoader {}Tumpukan memori area luapan
Untuk memicu overflow memori tumpukan, saya mengatur array byte 1MB di kelas dalam, dan pada saat yang sama, saya harus memanggil threadlocal.get () di blok statis. Hanya panggilan yang akan memicu InitialValue () untuk menginisialisasi objek bagian dalam, jika tidak saya hanya akan membuat objek threadlocal kosong, dan tidak ada data di ThreadLocalMap.
Parameter JVM adalah sebagai berikut:
-Xms100m -xmx100m -xx:+useparnewgc -xx:+useconcmarksweepgc -xx:+printgcdetails -xx:+printheapatgc -xx:+printclasshistogram -xx:+heapdumponOfmemoryerror
Setelah 814 eksekusi terakhir, overflows Area Heap JVM, seperti yang ditunjukkan di bawah ini:
java.lang.outofmemoryError: java heap spacedumping heap ke java_pid11824.hprof ... heap dump file dibuat [100661202 byte dalam 1.501 detik] heap par generasi baru 30720k, digunakan 30389k [0x0000000000000000C00C00C0000000000000000000000000000B, 0x50000K [0x000000000000000000000000000000000000000000000000 99% Digunakan [0x00000000F9C000000, 0x00000000FB6AD450, 0x00000000FB6B0000) Dari Space 3392K, 90% Digunakan [0x00000000000000000000002002, 0x00000000, 0x00002, 0x0000, 0x0000200, 0x000000, 0x000000, 0x000000, 0x0000, 0x000000, 0x0000, [0x0000000000FBA00000, 0x00000000FBD500000) Generasi SWEEP KONSURREN Total 68288K, digunakan 67600K [0x000000000FBD500000, 0x00000010000.000, 0x0000000100000) Metaspace yang Digunakan 3770k, Kapasitas 5100.000100000) Digunakan 3770k, Kapasitas 5100.000100000) Digunakan 3770k, Kapasitas 5100.000) Digunakan 3770k, 0x000000010000 Ruang kelas yang digunakan 474k, kapasitas 578k, berkomitmen 640k, dicadangkan 1048576Kexception di Thread "Thread-0" Java.lang.outofmemoryError: Java Heap Space di com.ezlippi.memoryleak $ dalam. <Clinit> (Memoryleak.java:34) sun.reflect.nativeConstructorAccessorImpl.newinstance0 (Metode Asli) di Sun.Reflect.nativeconstructorcessorImpl.newinstance (Sumber yang Tidak Diketahui) di Sun.reflect.DelegatingConstructorcacsecessImpl.newinstance (sumber yang tidak diketahui) di java.nreflect. di java.lang.reflect.constructor.newinstance (sumber yang tidak diketahui)) di java.lang.class.newinstance (sumber yang tidak diketahui) di com.ezlippi.memoryleak $ 1.run (memoryleak.java:20) di java.lang.thread.rread.rad.rad.roD.roma) di sumber) di java.lang.thread.rread.rad.rad.rad.raR.RoD.RArt.roD.roD.RArt.ROWN) di sumber) di java.lang.thread.rread.rread.rread.rread.rread.rad.rread.rread.rad.rad.rad.rad.rad.raRy
Anda dapat melihat bahwa JVM tidak memiliki memori untuk membuat objek dalam baru, karena area heap menyimpan banyak array byte 1MB. Di sini saya mencetak histogram kelas (gambar berikut adalah adegan dengan ukuran tumpukan 1024m), menghilangkan beberapa kelas yang tidak penting. Dapat dilihat bahwa array byte menempati 855m ruang, dan 814 instance com.ezlippi.memoryleak $ customclassloader dibuat, yang pada dasarnya sesuai dengan ukuran array byte:
Num #instances #bytes kelas Nama --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- com.ezlippi.memoryleak $ customclassloader 12: 820 53088 [ljava.util.hashtable $ entri; 15: 817 39216 java.util.hashtable 16: 915 36600 java.lang.ref.softreference 17: 543 34752 java.net.url 18: 697 33456 Java.nio.heapcharbuffer 19: 817 32 32. java.util.treemap $ entri 21: 928 29696 java.util.hashtable $ entri 22: 1802 28832 java.util.hashset 23: 817 26144 java.security.codesource 24: 814 26048 java.lang.throrecurity
Metaspace Overflow
Untuk membuat Metaspace overflow, Anda harus mengurangi ruang Metaspace sedikit, dan memuat cukup kelas sebelum tumpukan luapan. Oleh karena itu, saya menyesuaikan parameter JVM dan menyesuaikan ukuran array byte dengan 1KB, seperti yang ditunjukkan di bawah ini:
private byte[] KB = new byte[1024];-Xms100m -Xmx100m -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:+PrintClassHistogram -XX:MetaspaceSize=2m -XX:MaxMetaspaceSize=2m
Dari log GC, kita dapat melihat bahwa ketika Meraspace mencapai ambang GC (yaitu, ukuran konfigurasi maxmetaspacesze) FullGC akan dipicu:
java.lang.outofmemoryError: Metaspace << No Stack Trace Tersedia >> {Heap Sebelum GC Invocations = 20 (Lengkap 20): Par Generasi Baru Total 30720k, Digunakan 0K [0x0000000F9C000000, 0x0000000000.000) Eden Space 27328k, 0% 0x000000000) (0x0000000) eden 27328k, 0% 0x000000.000) [0x0000000) eden 27328k, 0%. 0x00000000f9c00000, 0x0000000f9c00000, 0x0000000f9c00000, 0x0000000fb6b0000) Dari Space 3392K, 0% Digunakan [0x0000000000FB6B0000, 0x0000002, 0x002.33B6B6B0000, 0X0000000000002.33B6B6B6B6B6B0000 [0x0000000000fba00000, 0x000000000fbd500000) to space 3392K, 0% used [0x0000000000fba00000, 0x000000010000000000) Metaspace used 1806K, capacity 1988K, committed 2048K, reserved 1056768K class space used 202K, capacity 384K, committed 384K, reserved 1048576K [GC penuh (ambang metadata GC) [CMSPROCESS selesai dengan kode keluar 1Dari contoh di atas, kita dapat melihat bahwa jika class loader dan threadlocal digunakan secara tidak benar, itu memang akan menyebabkan kebocoran memori. Kode Sumber Lengkap ada di GitHub