Bagaimana menangani kesalahan “java.lang.OutOfMemoryError: Java heap space”?

416

Saya menulis aplikasi Swing sisi klien (desainer font grafis) di Java 5 . Baru-baru ini, saya mengalami java.lang.OutOfMemoryError: Java heap spacekesalahan karena saya tidak konservatif dalam penggunaan memori. Pengguna dapat membuka file dalam jumlah tidak terbatas, dan program ini menyimpan objek yang dibuka di memori. Setelah penelitian cepat saya menemukan Ergonomi di Java Virtual Machine 5.0 dan yang lain mengatakan pada mesin Windows JVM default max heap size as 64MB.

Dengan situasi ini, bagaimana saya harus mengatasi kendala ini?

Saya dapat meningkatkan ukuran heap maks menggunakan opsi baris perintah ke java, tapi itu akan membutuhkan mencari tahu RAM yang tersedia dan menulis beberapa program peluncuran atau skrip. Selain itu, peningkatan hingga beberapa hingga pada akhirnya tidak menyingkirkan masalah tersebut.

Saya bisa menulis ulang beberapa kode saya untuk tetap mempertahankan objek ke sistem file (menggunakan database adalah hal yang sama) untuk membebaskan memori. Itu bisa berhasil, tapi mungkin itu juga banyak pekerjaan.

Jika Anda bisa mengarahkan saya ke detail ide di atas atau beberapa alternatif seperti memori virtual otomatis, memperluas ukuran tumpukan secara dinamis , itu akan menjadi hebat.

Eugene Yokota
sumber
Ukuran tumpukan maksimum default 64 MB adalah dari sebelum J2SE 5.0. Untuk informasi J2SE 8.0, lihat "Ergonomi Kolektor Sampah" di docs.oracle.com/javase/8/docs/technotes/guides/vm/… .
Andy Thomas
Jika Anda mendarat di sini karena setiap pertanyaan OOM ditiru untuk pertanyaan ini, pastikan Anda juga memeriksa: stackoverflow.com/questions/299659/… Ini memberikan solusi untuk membersihkan referensi memori 'tepat waktu' sebelum OOM. SoftReferences dapat menjadi alat yang memecahkan masalah Anda yang sebenarnya.
Steve Steiner

Jawaban:

244

Pada akhirnya Anda selalu memiliki tumpukan maksimum yang terbatas untuk digunakan, apa pun platform yang Anda jalankan. Di Windows 32 bit ini ada di sekitar 2GB(tidak secara khusus menumpuk tetapi jumlah total memori per proses). Kebetulan Java memilih untuk membuat default lebih kecil (mungkin sehingga programmer tidak dapat membuat program yang memiliki alokasi memori yang hilang tanpa mengalami masalah ini dan harus memeriksa dengan tepat apa yang mereka lakukan).

Jadi ini mengingat ada beberapa pendekatan yang dapat Anda ambil untuk menentukan jumlah memori yang Anda butuhkan atau untuk mengurangi jumlah memori yang Anda gunakan. Salah satu kesalahan umum dengan sampah yang dikumpulkan bahasa seperti Java atau C # adalah untuk tetap mencari referensi ke objek yang tidak lagi Anda gunakan, atau mengalokasikan banyak objek saat Anda bisa menggunakannya kembali . Selama objek memiliki referensi kepada mereka, mereka akan terus menggunakan ruang tumpukan karena pengumpul sampah tidak akan menghapusnya.

Dalam hal ini Anda dapat menggunakan profiler memori Java untuk menentukan metode apa dalam program Anda yang mengalokasikan sejumlah besar objek dan kemudian menentukan apakah ada cara untuk memastikan mereka tidak lagi direferensikan, atau untuk tidak mengalokasikannya di tempat pertama. Salah satu opsi yang saya gunakan di masa lalu adalah "JMP" http://www.khelekore.org/jmp/ .

Jika Anda menentukan bahwa Anda mengalokasikan objek-objek ini untuk suatu alasan dan Anda perlu mencari referensi (tergantung pada apa yang Anda lakukan ini mungkin terjadi), Anda hanya perlu meningkatkan ukuran tumpukan maksimum ketika Anda memulai program. Namun, begitu Anda melakukan profil memori dan memahami bagaimana objek Anda dialokasikan, Anda harus memiliki ide yang lebih baik tentang berapa banyak memori yang Anda butuhkan.

Secara umum jika Anda tidak dapat menjamin bahwa program Anda akan berjalan dalam sejumlah memori terbatas (mungkin tergantung pada ukuran input) Anda akan selalu mengalami masalah ini. Hanya setelah melelahkan semua ini Anda akan perlu melihat ke dalam objek caching ke disk dll. Pada titik ini Anda harus memiliki alasan yang sangat baik untuk mengatakan "Saya perlu Xgb memori" untuk sesuatu dan Anda tidak dapat mengatasinya dengan meningkatkan algoritma atau pola alokasi memori Anda. Secara umum ini biasanya hanya akan menjadi kasus untuk algoritma yang beroperasi pada dataset besar (seperti database atau beberapa program analisis ilmiah) dan kemudian teknik seperti caching dan memori dipetakan IO menjadi berguna.

Ben Childs
sumber
6
OpenJDK dan OracleJDK memiliki bundel profiler - jvisualvm. Jika Anda menginginkan kenyamanan lebih, saya sarankan komersial Yourkit.
Petr Gladkikh
121

Jalankan Java dengan opsi baris perintah -Xmx, yang menetapkan ukuran maksimum heap.

Lihat di sini untuk detailnya .

Dave Webb
sumber
3
Bagaimana cara mengatur parameter ini selamanya? Karena saya menggunakan perintah 'gradlew assemble'.
Dr.jacky
2
Run-> Run Configurations-> Klik pada argumen-> di dalam argumen VM ketik -Xms1g -Xmx2g
Arayan Singh
2
Itu jawaban yang sebenarnya.
nccc
85

Anda dapat menentukan per proyek berapa banyak tumpukan ruang yang diinginkan proyek Anda

Berikut ini untuk Eclipse Helios / Juno / Kepler :

Klik kanan mouse

 Run As - Run Configuration - Arguments - Vm Arguments, 

lalu tambahkan ini

-Xmx2048m
allenhwkim
sumber
1
hai bighostkim dan cuongHuyTo, di mana "Argumen" .. saya dapat melihat upto Menjalankan Konfigurasi. Tolong telepon saya. Kebutuhan saya untuk mengunduh dan menyimpan hampir 2000 kontak dari gmail. Itu macet karena kehabisan memori
AndroidRaji
@AndroiRaji: Anda klik kanan mouse Anda ke kelas Java yang memiliki runnable main (yaitu "public static void main (String [] args)"), lalu pilih Run As - Run Configuration. Kemudian "Argumen" adalah tab tepat setelah Utama (Anda melihat tab Main, Argumen, JRE, Classpath, Sumber, Lingkungan, Umum).
CuongHuyTo
47

Meningkatkan ukuran tumpukan bukan "memperbaiki" itu adalah "plester", 100% sementara. Ini akan crash lagi di tempat lain. Untuk menghindari masalah ini, tulis kode kinerja tinggi.

  1. Gunakan variabel lokal sedapat mungkin.
  2. Pastikan Anda memilih objek yang benar (EX: Seleksi antara String, StringBuffer dan StringBuilder)
  3. Gunakan sistem kode yang baik untuk program Anda (EX: Menggunakan variabel statis VS variabel non statis)
  4. Hal-hal lain yang bisa digunakan pada kode Anda.
  5. Cobalah untuk bergerak dengan THREADING multy
Jus lemon
sumber
Ini sangat benar. Saya mencoba untuk memperbaiki satu masalah di mana saya mendapatkan OOM pada utas AWT tetapi jika saya menggunakan utas baru yang berbeda, saya tidak mendapatkan masalah OOM. Yang bisa saya temukan online adalah meningkatkan ukuran tumpukan untuk AWT thread.
Ashish
@ Ash: Ya, perbaiki masalah inti alih-alih mencari plester.
Jus Lemon
Pengumpulan sampah dan pendekatan manajemen memori di Jawa seharusnya menyelesaikan semua komplikasi malloc-dealloc dari para pendahulunya :( Tentu saja saya sepenuhnya setuju dengan jawaban ini. Sayang sekali defaultnya tidak membuatnya mudah untuk menulis kode dengan data ramping -struktur yang dibersihkan SECEPATNYA
Davos
31

Peringatan besar ---- di kantor saya, kami menemukan (pada beberapa mesin windows) kami tidak dapat mengalokasikan lebih dari 512m untuk heap Java. Ini ternyata disebabkan oleh produk anti-virus Kaspersky yang diinstal pada beberapa mesin tersebut. Setelah mencopot pemasangan produk AV itu, kami menemukan bahwa kami dapat mengalokasikan setidaknya 1,6gb, yaitu -Xmx1600m(m wajib jika tidak akan menyebabkan kesalahan lain "Tumpukan awal yang terlalu kecil") berfungsi.

Tidak tahu apakah ini terjadi dengan produk AV lainnya, tetapi mungkin ini terjadi karena program AV menyimpan blok memori kecil di setiap ruang alamat, sehingga mencegah alokasi tunggal yang sangat besar.

David
sumber
22

Argumen VM bekerja untuk saya di gerhana. Jika Anda menggunakan eclipse versi 3.4, lakukan hal berikut

pergi ke Run --> Run Configurations -->kemudian pilih proyek di bawah maven build -> lalu pilih tab "JRE" -> lalu masukkan -Xmx1024m.

Atau Anda bisa melakukannya Run --> Run Configurations --> select the "JRE" tab -->lalu masuk -Xmx1024m

Ini harus meningkatkan tumpukan memori untuk semua build / proyek. Ukuran memori di atas adalah 1 GB. Anda dapat mengoptimalkan seperti yang Anda inginkan.

loveall
sumber
18

Ya, dengan -XmxAnda dapat mengonfigurasi lebih banyak memori untuk JVM Anda. Untuk memastikan bahwa Anda tidak membocorkan atau membuang memori. Ambil tumpukan sampah dan gunakan Eclipse Memory Analyzer untuk menganalisis konsumsi memori Anda.

Kohlerm
sumber
JVMJ9VM007E Opsi baris perintah tidak dikenal: -Xmx Tidak dapat membuat mesin virtual Java. Downvote
Philip Rego
17

Saya ingin menambahkan rekomendasi dari pemotretan masalah oracle artikel .

Pengecualian di utas thread_name: java.lang.OutOfMemoryError: Java heap space

Pesan detail Java heap space menunjukkan objek tidak dapat dialokasikan di heap Java. Kesalahan ini tidak selalu berarti kebocoran memori

Kemungkinan penyebab:

  1. Masalah konfigurasi sederhana , di mana ukuran tumpukan yang ditentukan tidak mencukupi untuk aplikasi.

  2. Aplikasi secara tidak sengaja memegang referensi ke objek , dan ini mencegah objek dari pengumpulan sampah.

  3. Penggunaan finalizer yang berlebihan .

Satu sumber potensial lain dari kesalahan ini muncul dengan aplikasi yang membuat penggunaan finalizer berlebihan. Jika kelas memiliki metode finalisasi, maka objek dari tipe itu tidak memiliki ruang mereka direklamasi pada waktu pengumpulan sampah

Setelah pengumpulan sampah , objek diantrikan untuk finalisasi , yang terjadi di lain waktu. finalizers dieksekusi oleh thread daemon yang melayani antrian finalisasi. Jika utas finalizer tidak dapat mengikuti antrian finalisasi, maka Java heap dapat terisi dan jenis pengecualian OutOfMemoryError ini akan dibuang.

Satu skenario yang dapat menyebabkan situasi ini adalah ketika aplikasi membuat utas prioritas tinggi yang menyebabkan antrian finalisasi meningkat pada tingkat yang lebih cepat daripada laju di mana benang finalizer sedang melayani antrian itu.

Ravindra babu
sumber
9

Ikuti langkah-langkah di bawah ini:

  1. Buka catalina.shdari tomcat / bin.

  2. Ubah JAVA_OPTS ke

    JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms1536m 
    -Xmx1536m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m 
    -XX:MaxPermSize=256m -XX:+DisableExplicitGC"
  3. Mulai ulang tomcat Anda

Pradip Bhatt
sumber
8

Saya membaca di tempat lain bahwa Anda dapat mencoba - menangkap java.lang.OutOfMemoryError dan di blok tangkap, Anda dapat membebaskan semua sumber daya yang Anda tahu mungkin menggunakan banyak memori, menutup koneksi dan sebagainya, lalu melakukan System.gc() kembali coba apa pun kamu akan lakukan.

Cara lain adalah ini walaupun, saya tidak tahu apakah ini akan berhasil, tetapi saya sedang menguji apakah ini akan berfungsi pada aplikasi saya.

Idenya adalah untuk melakukan pengumpulan Sampah dengan memanggil System.gc () yang dikenal untuk meningkatkan memori bebas. Anda dapat terus memeriksa ini setelah kode yang menelan memori dijalankan.

//Mimimum acceptable free memory you think your app needs
long minRunningMemory = (1024*1024);

Runtime runtime = Runtime.getRuntime();

if(runtime.freeMemory()<minRunningMemory)
 System.gc();
mwangi
sumber
6
Secara umum saya pikir JVM akan lebih suka Garbage Collect (GC) daripada membuang OutOfMemoryError. Secara eksplisit memanggil System.gc () setelah OutOfMemoryError mungkin membantu pada beberapa VM / konfigurasi, tapi saya tidak berharap itu berfungsi dengan baik dalam kasus umum. Namun, menjatuhkan referensi objek yang tidak perlu pasti akan membantu dalam hampir semua kasus.
Mike Clark
6
@mwangi Calling System.gc () langsung dari kode umumnya merupakan ide yang buruk. Ini hanya saran untuk JVM bahwa GC harus dilakukan, tetapi sama sekali tidak ada jaminan bahwa itu akan dilakukan.
7

Cara mudah untuk menyelesaikannya OutOfMemoryErrordi java adalah meningkatkan ukuran heap maksimum dengan menggunakan opsi JVM-Xmx512M , ini akan segera menyelesaikan OutOfMemoryError Anda. Ini adalah solusi pilihan saya ketika saya mendapatkan OutOfMemoryError di Eclipse, Maven atau ANT sambil membangun proyek karena berdasarkan pada ukuran proyek Anda dapat dengan mudah kehabisan Memori.

Berikut adalah contoh peningkatan ukuran heap maksimum JVM, Juga lebih baik untuk menjaga -Xmx ke -Xms ransum 1: 1 atau 1: 1,5 jika Anda mengatur ukuran heap dalam aplikasi java Anda.

export JVM_ARGS="-Xms1024m -Xmx1024m"

Tautan Referensi

chaukssey
sumber
1
Adakah ide mengapa kita harus menyimpannya dalam rasio 1: 1 atau 1: 1.5?
ernesto
7

Secara default untuk pengembangan, JVM menggunakan ukuran kecil dan konfigurasi kecil untuk fitur terkait kinerja lainnya. Tetapi untuk produksi Anda dapat menyetel misalnya (Selain itu konfigurasi khusus Application Server dapat ada) -> (Jika masih tidak cukup memori untuk memenuhi permintaan dan tumpukan sudah mencapai ukuran maksimum, OutOfMemoryError akan terjadi)

-Xms<size>        set initial Java heap size
-Xmx<size>        set maximum Java heap size
-Xss<size>        set java thread stack size

-XX:ParallelGCThreads=8
-XX:+CMSClassUnloadingEnabled
-XX:InitiatingHeapOccupancyPercent=70
-XX:+UnlockDiagnosticVMOptions
-XX:+UseConcMarkSweepGC
-Xms512m
-Xmx8192m
-XX:MaxPermSize=256m (in java 8 optional)

Sebagai contoh: Pada Platform linux untuk pengaturan mode produksi lebih disukai.

Setelah mengunduh dan mengkonfigurasi server dengan cara ini http://www.ehowstuff.com/how-to-install-and-setup-apache-tomcat-8-on-centos-7-1-rhel-7/

1. buat file setenv.sh pada folder / opt / tomcat / bin /

   touch /opt/tomcat/bin/setenv.sh

2.Buka dan tulis params ini untuk mengatur mode yang lebih disukai.

nano  /opt/tomcat/bin/setenv.sh 

export CATALINA_OPTS="$CATALINA_OPTS -XX:ParallelGCThreads=8"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+CMSClassUnloadingEnabled"
export CATALINA_OPTS="$CATALINA_OPTS -XX:InitiatingHeapOccupancyPercent=70"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UnlockDiagnosticVMOptions"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseConcMarkSweepGC"
export CATALINA_OPTS="$CATALINA_OPTS -Xms512m"
export CATALINA_OPTS="$CATALINA_OPTS -Xmx8192m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:MaxMetaspaceSize=256M"

3.service tomcat restart

Perhatikan bahwa JVM menggunakan lebih banyak memori daripada hanya tumpukan. Misalnya metode Java, tumpukan ulir dan pegangan asli dialokasikan dalam memori yang terpisah dari heap, serta struktur data internal JVM.

Musa
sumber
7

Saya telah menghadapi masalah yang sama dari ukuran tumpukan java.

Saya punya dua solusi jika Anda menggunakan java 5 (1.5).

  1. cukup instal jdk1.6 dan buka preferensi eclipse dan atur jalur jre jav1 1.6 seperti yang telah Anda instal.

  2. Periksa argumen VM Anda dan biarkan apa pun itu. cukup tambahkan satu baris di bawah ini dari semua argumen yang ada dalam argumen VM sebagai -Xms512m -Xmx512m -XX: MaxPermSize = ... m (192m).

Saya pikir itu akan berhasil ...

Soumya Sandeep Mohanty
sumber
7

Jika Anda perlu memantau penggunaan memori Anda saat runtime, java.lang.managementpaket penawaran MBeansyang dapat digunakan untuk memantau kumpulan memori di VM Anda (misalnya, ruang eden, generasi bertenor dll), dan juga perilaku pengumpulan sampah.

Ruang heap gratis yang dilaporkan oleh MBeans ini akan sangat bervariasi tergantung pada perilaku GC, terutama jika aplikasi Anda menghasilkan banyak objek yang nantinya merupakan GC-ed. Salah satu pendekatan yang mungkin adalah memantau ruang tumpukan kosong setelah setiap GC penuh, yang mungkin dapat Anda gunakan untuk membuat keputusan membebaskan memori dengan benda-benda yang ada.

Pada akhirnya, taruhan terbaik Anda adalah membatasi retensi memori Anda sejauh mungkin sementara kinerja tetap dapat diterima. Seperti komentar sebelumnya dicatat, memori selalu terbatas, tetapi aplikasi Anda harus memiliki strategi untuk mengatasi kelelahan memori.

Leigh
sumber
5

Perhatikan bahwa jika Anda memerlukan ini dalam situasi penerapan, pertimbangkan untuk menggunakan Java WebStart (dengan versi "ondisk", bukan yang satu jaringan - mungkin di Java 6u10 dan yang lebih baru) karena memungkinkan Anda untuk menentukan berbagai argumen untuk JVM dalam tanda silang. cara platform.

Kalau tidak, Anda akan memerlukan peluncur khusus sistem operasi yang menetapkan argumen yang Anda butuhkan.

Thorbjørn Ravn Andersen
sumber
Java WebStart sedang dihapus. Saya belum mengetahui penggantian yang cocok.
Thorbjørn Ravn Andersen
1

Jika masalah ini terjadi di Wildfly 8 dan JDK1.8, maka kita perlu menentukan pengaturan MaxMetaSpace daripada pengaturan PermGen.

Sebagai contoh kita perlu menambahkan konfigurasi di bawah ini di file setenv.sh dari wildfly. JAVA_OPTS="$JAVA_OPTS -XX:MaxMetaspaceSize=256M"

Untuk informasi lebih lanjut, silakan periksa Wildfly Heap Issue

satish
sumber
1

Mengenai netbeans, Anda bisa mengatur ukuran heap maks untuk menyelesaikan masalah.

Pergi ke 'Jalankan', lalu -> 'Atur Konfigurasi Proyek' -> 'Kustomisasi' -> 'jalankan' dari jendela yang muncul -> 'Opsi VM' -> isi '-Xms2048m -Xmx2048m' .

Xiaogang
sumber
1

Jika Anda terus mengalokasikan & menyimpan referensi ke objek, Anda akan mengisi jumlah memori yang Anda miliki.

Salah satu opsi adalah melakukan file transparan tutup & buka ketika mereka beralih tab (Anda hanya menyimpan pointer ke file, dan ketika pengguna beralih tab, Anda menutup & membersihkan semua objek ... itu akan membuat perubahan file lebih lambat ... tapi ...), dan mungkin hanya menyimpan 3 atau 4 file di memori.

Hal lain yang harus Anda lakukan adalah, ketika pengguna membuka file, memuatnya, dan mencegat OutOfMemoryError, kemudian (karena tidak mungkin untuk membuka file) tutup file itu, bersihkan objeknya dan peringatkan pengguna bahwa ia harus menutup yang tidak digunakan. file.

Gagasan Anda untuk memperluas memori virtual secara dinamis tidak menyelesaikan masalah, karena mesin terbatas pada sumber daya, jadi Anda harus berhati-hati & menangani masalah memori (atau setidaknya, berhati-hatilah dengan mereka).

Beberapa petunjuk yang pernah saya lihat dengan kebocoran memori adalah:

-> Ingatlah bahwa jika Anda memasukkan sesuatu ke dalam koleksi dan kemudian melupakannya, Anda masih memiliki referensi yang kuat untuk itu, jadi batalkan koleksi itu, bersihkan atau lakukan sesuatu dengannya ... jika tidak, Anda akan menemukan kebocoran memori sulit ditemukan.

-> Mungkin, menggunakan koleksi dengan referensi yang lemah (weakhashmap ...) dapat membantu dengan masalah memori, tetapi Anda harus berhati-hati dengannya, karena Anda mungkin menemukan bahwa objek yang Anda cari telah dikumpulkan.

-> Gagasan lain yang saya temukan adalah untuk mengembangkan koleksi persisten yang disimpan pada objek basis data yang paling sedikit digunakan dan dimuat secara transparan. Ini mungkin akan menjadi pendekatan terbaik ...

SoulWanderer
sumber
0

Jika semuanya gagal, selain meningkatkan ukuran heap maks, cobalah juga meningkatkan ukuran swap. Untuk Linux, seperti yang sekarang, instruksi yang relevan dapat ditemukan di https://linuxize.com/post/create-a-linux-swap-file/ .

Ini dapat membantu jika Anda misalnya mengkompilasi sesuatu yang besar di platform yang disematkan.

nccc
sumber