Cara menemukan Kebocoran Memori Java


142

Bagaimana Anda menemukan kebocoran memori di Java (menggunakan, misalnya, JHat)? Saya telah mencoba memuat tumpukan heap di JHat untuk melihat dasar. Namun, saya tidak mengerti bagaimana saya seharusnya dapat menemukan referensi root ( ref ) atau apa pun namanya. Pada dasarnya, saya dapat mengatakan bahwa ada beberapa ratus megabyte entri tabel hash ([java.util.HashMap $ Entri atau sesuatu seperti itu), tetapi peta digunakan di semua tempat ... Apakah ada cara untuk mencari peta besar , atau mungkin menemukan akar umum dari pohon objek besar?

[Sunting] Ok, saya sudah membaca jawabannya sejauh ini tetapi katakan saja saya bajingan yang murah (artinya saya lebih tertarik mempelajari cara menggunakan JHat daripada membayar untuk JProfiler). Selain itu, JHat selalu tersedia karena merupakan bagian dari JDK. Kecuali tentu saja tidak ada cara dengan JHat selain kekerasan, tapi aku tidak percaya itu bisa terjadi.

Juga, saya tidak berpikir saya akan dapat benar-benar memodifikasi (menambahkan logging dari semua ukuran peta) dan menjalankannya cukup lama untuk saya perhatikan kebocorannya.


Ini adalah "suara" lain untuk JProfiler. Ini bekerja cukup baik untuk analisis tumpukan, memiliki UI yang layak, dan bekerja dengan cukup baik. Seperti yang dikatakan McKenzieG1, $ 500 lebih murah daripada jumlah waktu yang seharusnya Anda bakar untuk mencari sumber kebocoran ini. Sejauh harga alat, itu tidak buruk.
joev

Oracle memiliki halaman yang relevan di sini: docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/…
Laurel

Jawaban:


126

Saya menggunakan pendekatan berikut untuk menemukan kebocoran memori di Jawa. Saya telah menggunakan jProfiler dengan sangat sukses, tetapi saya percaya bahwa setiap alat khusus dengan kemampuan grafik (diffs lebih mudah untuk dianalisis dalam bentuk grafis) akan berfungsi.

  1. Mulai aplikasi dan tunggu hingga status "stabil", ketika semua inisialisasi selesai dan aplikasi idle.
  2. Jalankan operasi yang diduga menghasilkan kebocoran memori beberapa kali untuk memungkinkan cache apa pun, inisialisasi terkait-DB terjadi.
  3. Jalankan GC dan ambil snapshot memori.
  4. Jalankan kembali operasi. Tergantung pada kompleksitas operasi dan ukuran data yang diproses operasi mungkin perlu dijalankan beberapa kali.
  5. Jalankan GC dan ambil snapshot memori.
  6. Jalankan diff untuk 2 foto dan menganalisisnya.

Pada dasarnya analisis harus dimulai dari perbedaan positif terbesar dengan, katakanlah, jenis-jenis objek dan temukan apa yang menyebabkan objek-objek ekstra itu melekat di memori.

Untuk aplikasi web yang memproses permintaan dalam beberapa analisis utas menjadi lebih rumit, namun demikian pendekatan umum masih berlaku.

Saya melakukan sejumlah proyek yang secara khusus bertujuan mengurangi jejak memori aplikasi dan pendekatan umum ini dengan beberapa penyesuaian dan trik khusus aplikasi selalu bekerja dengan baik.


7
Sebagian besar (jika tidak semua) profiler Java memberi Anda opsi untuk memanggil GC dengan mengklik tombol. Atau Anda dapat memanggil System.gc () dari tempat yang sesuai dalam kode Anda.
Dima Malenko

3
Bahkan jika kita memanggil System.gc () JVM dapat memilih untuk mengabaikan panggilan. AFAIK ini khusus JVM. Memberi +1 pada jawabannya.
Aniket Thakur

4
Apa sebenarnya "snapshot memori?" Apakah ada sesuatu yang akan memberi tahu saya jumlah setiap jenis objek yang kode saya jalankan?
Ditakdirkan

2
Bagaimana saya beralih dari "mulai dari perbedaan positif terbesar dengan tipe objek" ke "menemukan apa yang menyebabkan objek ekstra itu menempel di memori"? Saya melihat hal-hal yang sangat umum seperti int [], Obyek [], String, dll. Bagaimana saya menemukan dari mana mereka berasal?
Vituel

48

Penanya di sini, saya harus mengatakan mendapatkan alat yang tidak membutuhkan waktu 5 menit untuk menjawab klik apa pun membuatnya lebih mudah untuk menemukan potensi kebocoran memori.

Karena orang menyarankan beberapa alat (saya hanya mencoba visual wm sejak saya mendapatkannya dalam uji coba JDK dan JProbe) saya pikir saya harus menyarankan alat sumber bebas / terbuka yang dibangun pada platform Eclipse, Memory Analyzer (kadang-kadang disebut sebagai memori SAP analyzer) tersedia di http://www.eclipse.org/mat/ .

Apa yang benar-benar keren tentang alat ini adalah mengindeks tumpukan heap ketika saya pertama kali membukanya yang memungkinkannya untuk menampilkan data seperti retakan heap tanpa menunggu 5 menit untuk setiap objek (hampir semua operasi adalah ton lebih cepat daripada alat lain yang saya coba) .

Saat Anda membuka tempat sampah, layar pertama menampilkan bagan pai dengan objek terbesar (menghitung tumpukan yang tersisa) dan seseorang dapat dengan cepat menavigasi ke objek yang besar untuk kenyamanan. Ini juga memiliki Temukan kemungkinan kebocoran yang menurut saya bisa berguna, tetapi karena navigasi sudah cukup bagi saya, saya tidak benar-benar masuk ke dalamnya.


1
Patut dicatat: tampaknya di Java 5 dan di atas, HeapDumpOnCtrlBreakparameter VM tidak tersedia . Solusi yang saya temukan (sejauh ini, masih mencari) adalah dengan menggunakan JMap untuk membuang .hproffile, yang kemudian saya masukkan ke Eclipse dan gunakan MAT untuk memeriksa.
Ben

1
Mengenai mendapatkan heap dump, sebagian besar profiler (termasuk JVisualVM) menyertakan opsi untuk membuang heap dan thread ke file.
bbaja42

13

Alat adalah bantuan besar.

Namun, ada kalanya Anda tidak dapat menggunakan alat: tumpukan timbunan sangat besar sehingga merusak alat, Anda mencoba memecahkan masalah mesin di beberapa lingkungan produksi yang Anda hanya memiliki akses shell, dll.

Dalam hal ini, ada baiknya untuk mengetahui cara Anda di sekitar file dump hprof.

Cari SITUS DIMULAI. Ini menunjukkan objek apa yang paling banyak menggunakan memori. Tetapi objek tidak disatukan bersama hanya berdasarkan tipe: setiap entri juga menyertakan ID "jejak". Anda kemudian dapat mencari "TRACE nnnn" untuk melihat beberapa frame teratas dari tumpukan tempat objek dialokasikan. Seringkali, begitu saya melihat di mana objek dialokasikan, saya menemukan bug dan saya selesai. Juga, perhatikan bahwa Anda dapat mengontrol berapa banyak frame yang direkam dalam tumpukan dengan opsi untuk -Xrunhprof.

Jika Anda memeriksa situs alokasi, dan tidak melihat ada yang salah, Anda harus mulai merantai ke belakang dari beberapa objek langsung ke root objek, untuk menemukan rantai referensi yang tidak terduga. Di sinilah alat sangat membantu, tetapi Anda dapat melakukan hal yang sama dengan tangan (well, with grep). Tidak hanya ada satu objek root (yaitu, objek tidak tunduk pada pengumpulan sampah). Thread, kelas, dan bingkai stack bertindak sebagai objek root, dan apa pun yang mereka referensikan kuat tidak dapat dikoleksi.

Untuk melakukan chaining, lihat di bagian HEAP DUMP untuk entri dengan id jejak buruk. Ini akan membawa Anda ke entri OBJ atau ARR, yang menunjukkan pengidentifikasi objek unik dalam heksadesimal. Cari semua kemunculan id itu untuk menemukan siapa yang punya referensi kuat ke objek tersebut. Ikuti setiap jalan ke belakang saat mereka bercabang sampai Anda mengetahui di mana kebocorannya. Lihat mengapa alat sangat berguna?

Anggota statis adalah pelanggar berulang karena kebocoran memori. Bahkan, bahkan tanpa alat, itu akan bernilai menghabiskan beberapa menit melihat kode Anda untuk anggota Peta statis. Bisakah peta menjadi besar? Apakah ada yang pernah membersihkan entri?


“Heap dump sangat besar sehingga menabrak alat” - terakhir saya periksa, jhatdan MATtampaknya mencoba memuat seluruh heap dump ke dalam memori, dan biasanya menabrak OutOfMemoryErrorpada dump besar (yaitu, dari aplikasi yang paling membutuhkan analisis tumpukan! ). NetBeans Profiler tampaknya menggunakan algoritma yang berbeda untuk mengindeks referensi yang bisa lambat pada dump besar tetapi setidaknya tidak mengkonsumsi memori tidak terbatas dalam alat dan crash.
Jesse Glick

10

Sebagian besar waktu, dalam aplikasi perusahaan tumpukan Java yang diberikan lebih besar dari ukuran ideal maksimal 12 hingga 16 GB. Saya merasa sulit untuk membuat profiler NetBeans bekerja langsung pada aplikasi-aplikasi java besar ini.

Tetapi biasanya ini tidak diperlukan. Anda dapat menggunakan utilitas jmap yang datang dengan jdk untuk mengambil heap dump "hidup", yaitu jmap akan membuang heap setelah menjalankan GC. Lakukan beberapa operasi pada aplikasi, tunggu sampai operasi selesai, lalu ambil tumpukan heap "langsung". Gunakan alat seperti Eclipse MAT untuk memuat heapdumps, mengurutkan pada histogram, melihat objek mana yang telah meningkat, atau mana yang tertinggi, Ini akan memberikan petunjuk.

su  proceeuser
/bin/jmap -dump:live,format=b,file=/tmp/2930javaheap.hrpof 2930(pid of process)

Hanya ada satu masalah dengan pendekatan ini; Tumpukan tumpukan besar, bahkan dengan opsi live, mungkin terlalu besar untuk dipindahkan ke putaran pengembangan, dan mungkin memerlukan mesin dengan memori / RAM yang cukup untuk dibuka.

Di situlah histogram kelas muncul. Anda dapat membuang histogram kelas langsung dengan alat jmap. Ini hanya akan memberikan histogram kelas penggunaan memori. Pada dasarnya itu tidak akan memiliki informasi untuk rantai referensi. Sebagai contoh dapat menempatkan array char di bagian atas. Dan kelas String di suatu tempat di bawah ini. Anda harus menggambar koneksi sendiri.

jdk/jdk1.6.0_38/bin/jmap -histo:live 60030 > /tmp/60030istolive1330.txt

Alih-alih mengambil dua heap dump, ambil dua histogram kelas, seperti yang dijelaskan di atas; Kemudian membandingkan histogram kelas dan melihat kelas-kelas yang meningkat. Lihat apakah Anda dapat menghubungkan kelas Java ke kelas aplikasi Anda. Ini akan memberikan petunjuk yang cukup bagus. Berikut ini adalah skrip python yang dapat membantu Anda membandingkan dua dump histogram jmap. histogramparser.py

Akhirnya alat seperti JConolse dan VisualVm sangat penting untuk melihat pertumbuhan memori dari waktu ke waktu, dan melihat apakah ada kebocoran memori. Akhirnya kadang-kadang masalah Anda mungkin bukan kebocoran memori, tetapi penggunaan memori yang tinggi. Untuk ini memungkinkan GC logging, gunakan GC pemadatan yang lebih maju dan baru seperti G1GC; dan Anda dapat menggunakan alat jdk seperti jstat untuk melihat langsung perilaku GC

jstat -gccause pid <optional time interval>

Referensi lain ke google untuk -jhat, jmap, GC Penuh, alokasi Humongous, G1GC


1
menambahkan posting blog dengan rincian lebih lanjut di sini - alexpunnen.blogspot.in/2015/06/...
Alex Punnen

5

Ada alat yang akan membantu Anda menemukan kebocoran Anda, seperti JProbe, YourKit, AD4J atau JRockit Mission Control. Yang terakhir adalah yang paling saya kenal secara pribadi. Alat apa pun yang baik harus membiarkan Anda menelusuri ke tingkat di mana Anda dapat dengan mudah mengidentifikasi kebocoran apa, dan di mana objek bocor dialokasikan.

Menggunakan HashTables, Hashmaps atau yang serupa adalah salah satu dari beberapa cara Anda dapat membocorkan memori di Java secara bersamaan. Jika saya harus menemukan kebocoran dengan tangan saya akan mencetak ukuran HashMaps saya, dan dari sana menemukan yang mana saya menambahkan item dan lupa untuk menghapusnya.


4

Ya, selalu ada solusi berteknologi rendah untuk menambahkan pencatatan ukuran peta Anda saat Anda memodifikasinya, lalu cari log yang petanya tumbuh melebihi ukuran yang wajar.



0

Anda benar-benar perlu menggunakan profiler memori yang melacak alokasi. Lihatlah JProfiler - fitur "heap walker" mereka sangat bagus, dan mereka memiliki integrasi dengan semua IDE Java utama. Ini tidak gratis, tetapi juga tidak semahal itu ($ 499 untuk satu lisensi) - Anda akan menghabiskan waktu senilai $ 500 dengan cepat berjuang untuk menemukan kebocoran dengan alat yang kurang canggih.


0

Anda dapat mengetahuinya dengan mengukur ukuran penggunaan memori setelah memanggil pengumpul sampah beberapa kali:

Runtime runtime = Runtime.getRuntime();

while(true) {
    ...
    if(System.currentTimeMillis() % 4000 == 0){
        System.gc();
        float usage = (float) (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024;
        System.out.println("Used memory: " + usage + "Mb");
    }

}

Jika nomor output sama, tidak ada kebocoran memori dalam aplikasi Anda, tetapi jika Anda melihat perbedaan antara jumlah penggunaan memori (semakin banyak), ada kebocoran memori di proyek Anda. Sebagai contoh:

Used memory: 14.603279Mb
Used memory: 14.737213Mb
Used memory: 14.772224Mb
Used memory: 14.802681Mb
Used memory: 14.840599Mb
Used memory: 14.900841Mb
Used memory: 14.942261Mb
Used memory: 14.976143Mb

Perhatikan bahwa terkadang diperlukan waktu untuk melepaskan memori dengan beberapa tindakan seperti streaming dan soket. Anda tidak boleh menilai dengan output pertama, Anda harus mengujinya dalam jumlah waktu tertentu.


0

Lihat layar ini untuk mencari kebocoran memori dengan JProfiler. Ini penjelasan visual dari Jawab Malima.

Catatan: Meskipun JProfiler bukan freeware, tetapi versi Trial dapat menangani situasi saat ini.


0

Karena kebanyakan dari kita sudah menggunakan Eclipse untuk menulis kode, Mengapa tidak menggunakan Memory Analyzer Tool (MAT) di Eclipse. Ini bekerja dengan baik.

The Eclipse MAT adalah satu set plug-in untuk Eclipse IDE yang menyediakan alat untuk menganalisis heap dumpsdari aplikasi Java dan untuk mengidentifikasi memory problemsdalam aplikasi.

Ini membantu pengembang untuk menemukan kebocoran memori dengan fitur-fitur berikut

  1. Mengambil snapshot memori (Heap Dump)
  2. Histogram
  3. Retakan Tumpukan
  4. Pohon Dominator
  5. Menjelajahi Jalur ke Root GC
  6. Inspektur
  7. Memori Umum Anti-Pola
  8. Bahasa Permintaan Obyek

masukkan deskripsi gambar di sini

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.