Kapan System.gc () melakukan sesuatu?

118

Saya tahu bahwa pengumpulan sampah dilakukan secara otomatis di Java. Tetapi saya mengerti bahwa jika Anda memanggil System.gc()kode Anda, JVM mungkin atau mungkin tidak memutuskan untuk melakukan pengumpulan sampah pada saat itu. Bagaimana tepatnya ini bekerja? Atas dasar / parameter apa JVM memutuskan untuk melakukan (atau tidak melakukan) GC ketika JVM melihatnya System.gc()?

Adakah contoh di mana ide yang baik untuk memasukkan ini ke dalam kode Anda?

Michael
sumber
4
Konsep terlalu rumit untuk didetailkan
GEOCHET
Perilaku yang tepat terserah JVM, yaitu bergantung pada implementasi.
Thorbjørn Ravn Andersen

Jawaban:

59

Dalam praktiknya, biasanya memutuskan untuk melakukan pengumpulan sampah. Jawabannya bervariasi tergantung pada banyak faktor, seperti JVM yang Anda jalankan, modenya, dan algoritma pengumpulan sampah yang digunakannya.

Saya tidak akan bergantung padanya dalam kode Anda. Jika JVM akan melontarkan OutOfMemoryError, memanggil System.gc () tidak akan menghentikannya, karena pengumpul sampah akan berusaha membebaskan sebanyak mungkin sebelum menjadi ekstrim. Satu-satunya saat saya melihatnya digunakan dalam praktik adalah di IDE di mana ia dilampirkan ke tombol yang dapat diklik pengguna, tetapi bahkan di sana itu tidak terlalu berguna.

jodonnell
sumber
3
Anda dapat memaksa pengumpulan sampah di jconsole
Benedikt Waldvogel
25
@bene Bisakah Anda benar-benar "memaksanya"? Apakah jconsole hanya memanggil System.gc ()?
John Meagher
4
Saya akan gunakan System.gc()saat berada di layar pemuatan untuk video game. Pada titik itu, saya tidak akan terlalu peduli jika panggilan itu menghapus semua yang mungkin bisa dilakukan, atau tidak melakukan apa pun. Saya akan, bagaimanapun, lebih suka bahwa itu "mengeluarkan upaya untuk mendaur ulang objek yang tidak digunakan" selama layar pemuatan, daripada selama bermain game.
Kröw
30

Satu-satunya contoh yang dapat saya pikirkan di mana masuk akal untuk memanggil System.gc () adalah ketika membuat profil aplikasi untuk mencari kemungkinan kebocoran memori. Saya yakin para profiler memanggil metode ini sebelum mengambil snapshot memori.

Guillermo Vasconcelos
sumber
4
Ya, itulah yang saya lakukan juga. Nilai yang dikembalikan oleh Runtime.freeMemory () misalnya hanya benar-benar berarti setelah System.gc (), karena biasanya Anda ingin mengetahui berapa banyak memori yang diblokir oleh instance yang tidak dapat dikumpulkan. Hanya untuk digunakan dalam debugging / memory profiling saja, tidak pernah dalam produksi.
sleske
Tidak, itu bagus untuk banyak skenario lainnya juga. Misalnya, katakanlah Anda memiliki satu pola yang sadar siklus proses. Di onStop () jika Anda null (ing) instance, ada baiknya memanggil System.gc () untuk membantunya.
portfoliobuilder
26

Spesifikasi Bahasa Java tidak menjamin bahwa JVM akan memulai GC saat Anda memanggil System.gc(). Inilah alasannya "mungkin atau mungkin tidak memutuskan untuk melakukan GC pada saat itu".

Sekarang, jika Anda melihat kode sumber OpenJDK , yang merupakan tulang punggung Oracle JVM, Anda akan melihat bahwa panggilan untuk System.gc()memulai siklus GC. Jika Anda menggunakan JVM lain, seperti J9, Anda harus memeriksa dokumentasinya untuk mengetahui jawabannya. Misalnya, JVM Azul memiliki pengumpul sampah yang berjalan terus menerus, jadi panggilan ke System.gc()tidak akan melakukan apa pun

Beberapa jawaban lain menyebutkan memulai GC di JConsole atau VisualVM. Pada dasarnya, alat ini melakukan panggilan jarak jauh System.gc().

Biasanya, Anda tidak ingin memulai siklus pengumpulan sampah dari kode Anda, karena itu mengacaukan semantik aplikasi Anda. Aplikasi Anda melakukan beberapa hal bisnis, JVM menangani manajemen memori. Anda harus memisahkan masalah tersebut (jangan membuat aplikasi Anda melakukan manajemen memori, fokuslah pada bisnis).

Namun, ada beberapa kasus di mana panggilan ke System.gc()mungkin dapat dimengerti. Pertimbangkan, misalnya, tanda mikro. Tidak seorang pun ingin siklus GC terjadi di tengah-tengah microbenchmark. Jadi, Anda dapat memicu siklus GC di antara setiap pengukuran untuk memastikan setiap pengukuran dimulai dengan heap kosong.

Pierre Laporte
sumber
26

Anda tidak memiliki kendali atas GC di java - VM memutuskan. Aku tidak pernah berjalan di kasus mana System.gc()yang diperlukan . Karena System.gc()panggilan hanya MENYARANKAN bahwa VM melakukan pengumpulan sampah dan juga melakukan pengumpulan sampah LENGKAP (generasi lama dan baru dalam heap multi-generasi), maka ini sebenarnya dapat menyebabkan LEBIH BANYAK siklus cpu yang dikonsumsi daripada yang diperlukan.

Dalam beberapa kasus, mungkin masuk akal untuk menyarankan VM agar melakukan pengumpulan penuh SEKARANG karena Anda mungkin tahu bahwa aplikasi akan menganggur selama beberapa menit ke depan sebelum pengangkatan berat terjadi. Misalnya, tepat setelah inisialisasi banyak objek sementara selama startup aplikasi (yaitu, saya baru saja menyimpan banyak info dalam cache, dan saya tahu saya tidak akan mendapatkan banyak aktivitas selama satu menit atau lebih). Pikirkan IDE seperti memulai gerhana - ia melakukan banyak hal untuk diinisialisasi, jadi mungkin segera setelah inisialisasi, masuk akal untuk melakukan gc penuh pada saat itu.

DustinB
sumber
17

Anda harus sangat berhati-hati jika menelepon System.gc(). Memanggilnya dapat menambah masalah kinerja yang tidak perlu ke aplikasi Anda, dan tidak ada jaminan untuk benar-benar melakukan pengumpulan. Sebenarnya mungkin untuk menonaktifkan eksplisit System.gc()melalui argumen java -XX:+DisableExplicitGC.

Saya sangat merekomendasikan membaca dokumen yang tersedia di Pengumpulan Sampah HotSpot Java untuk detail lebih mendalam tentang pengumpulan sampah.

David Schlosnagle
sumber
Aplikasi android saya selalu memberi saya OutOfMemoryException. Jadi saya berpikir untuk menggunakan System.gc () dalam fungsi onDestroy kelas saya untuk menghapus semua referensi dan objek yang tidak diinginkan. Apakah ini pendekatan yang baik
Sagar Devanga
2
@Sagar Devanga Memanggil gc secara manual seperti yang Anda gambarkan sepertinya tidak akan membantu. Sebelum melakukan OOM, JVM akan mencoba mengumpulkan sampah untuk mengosongkan memori
Scrubbie
10

System.gc()diterapkan oleh VM, dan yang dilakukannya adalah spesifik penerapannya. Pelaksana dapat dengan mudah kembali dan tidak melakukan apa-apa, misalnya.

Mengenai kapan harus mengeluarkan koleksi manual, satu-satunya saat Anda mungkin ingin melakukannya adalah ketika Anda meninggalkan koleksi besar yang berisi banyak koleksi yang lebih kecil - Map<String,<LinkedList>> misalnya - dan Anda ingin mencoba dan mengambil hasil kinerja saat itu dan di sana, tetapi untuk sebagian besar, Anda tidak perlu khawatir tentang itu. GC lebih tahu dari Anda - sayangnya - sebagian besar waktu.

Patrick
sumber
8

Jika Anda menggunakan buffer memori langsung, JVM tidak menjalankan GC untuk Anda meskipun memori langsung Anda hampir habis.

Jika Anda memanggil ByteBuffer.allocateDirect()dan Anda mendapatkan OutOfMemoryError Anda dapat menemukan panggilan ini baik-baik saja setelah memicu GC secara manual.

Peter Lawrey
sumber
Apakah Anda mengatakan bahwa System.gc () mengumpulkan alokasi langsung sedangkan JVM biasanya tidak (sebelum melempar OOM). Karena menurut saya itu salah. Satu-satunya alasan alokasi mungkin berhasil setelah GC eksplisit adalah waktu tambahan dan kemungkinan lebih banyak objek dalam heap dengan finalizer dibebaskan (dan membebaskan beberapa objek yang dialokasikan secara langsung).
eckes
Dalam versi terbaru Java 6, kode dalam alocateBuffer akan selalu menjalankan System.gc () sebelum melemparkan OutOfMemoryError. Dalam kode finalisasi itu memiliki jalan pintas Cleanersyang digunakan memori langsung. yaitu tidak dibebaskan oleh utas finaliser karena ini bisa terlalu lambat. Meskipun demikian, OOME dapat diperoleh jika Anda membuat region memori langsung dengan sangat cepat. Solusinya adalah tidak melakukan itu dan menggunakan kembali wilayah memori langsung Anda di mana Anda bisa.
Peter Lawrey
1
terima kasih, ini terdengar lebih seperti pengalaman saya. Jadi saya kira jawabannya hanya benar untuk versi Java yang lebih lama.
eckes
5

Kebanyakan JVM akan memulai GC (bergantung pada sakelar -XX: DiableExplicitGC dan -XX: + ExplicitGCInvokesConcurrent). Tetapi spesifikasinya kurang didefinisikan dengan baik untuk memungkinkan implementasi yang lebih baik di kemudian hari.

Spesifikasi membutuhkan klarifikasi: Bug # 6668279: (spec) System.gc () harus menunjukkan bahwa kami tidak merekomendasikan penggunaan dan tidak menjamin perilaku

Secara internal, metode gc digunakan oleh RMI dan NIO, dan mereka membutuhkan eksekusi sinkron, yang: saat ini sedang dibahas:

Bug # 5025281: Izinkan System.gc () untuk memicu koleksi lengkap serentak (bukan stop-the-world)

eckes
sumber
3

Garbage Collectionbagus Java, jika kita menjalankan Software yang dikodekan di java di Desktop / laptop / server. Anda dapat menelepon System.gc()atau Runtime.getRuntime().gc()masukJava .

Perhatikan bahwa tidak satu pun dari panggilan tersebut dijamin untuk melakukan apa pun. Itu hanya saran bagi jvm untuk menjalankan Pengumpul Sampah. Terserah JVM apakah itu menjalankan GC atau tidak. Jadi, jawaban singkatnya: kami tidak tahu kapan itu berjalan. Jawaban yang lebih panjang: JVM akan menjalankan gc jika ada waktu untuk itu.

Saya yakin, hal yang sama berlaku untuk Android. Namun, ini mungkin memperlambat sistem Anda.

Naveen
sumber
JVM akan menjalankan gc jika ada waktu untuk itu : tidak selalu benar, Jvm dapat menjalankan gc jika memori Eden sudah penuh.
abdelmouheimen
2

Biasanya, VM akan melakukan pengumpulan sampah secara otomatis sebelum melontarkan OutOfMemoryException, jadi menambahkan panggilan eksplisit seharusnya tidak membantu kecuali karena hal itu mungkin memindahkan klik kinerja ke momen waktu yang lebih awal.

Namun, saya rasa saya menemukan kasus yang mungkin relevan. Saya tidak yakin, karena saya belum menguji apakah itu berpengaruh:

Saat Anda memetakan memori file, saya yakin panggilan map () melontarkan IOException saat blok memori yang cukup besar tidak tersedia. Pengumpulan sampah sebelum file map () mungkin membantu mencegahnya, saya kira. Bagaimana menurut anda?


sumber
2

kami tidak pernah bisa memaksa pengumpulan sampah. System.gc hanya menyarankan vm untuk pengumpulan sampah, namun sebenarnya jam berapa mekanisme tersebut berjalan, tidak ada yang tahu, ini seperti yang dinyatakan oleh spesifikasi JSR.

lwpro2
sumber
2

Ada BANYAK yang bisa dikatakan dalam meluangkan waktu untuk menguji berbagai pengaturan pengumpulan sampah, tetapi seperti yang disebutkan di atas biasanya tidak berguna untuk melakukannya.

Saat ini saya mengerjakan proyek yang melibatkan lingkungan terbatas memori dan jumlah data yang relatif besar - ada beberapa bagian besar data yang mendorong lingkungan saya ke batasnya, dan meskipun saya dapat menurunkan penggunaan memori begitu bahwa secara teori seharusnya berfungsi dengan baik, saya masih akan mendapatkan error ruang heap --- opsi GC yang bertele-tele menunjukkan kepada saya bahwa ia mencoba mengumpulkan sampah, tetapi tidak berhasil. Dalam debugger, saya dapat menjalankan System.gc () dan cukup yakin akan ada "banyak" memori yang tersedia ... tidak banyak tambahan, tetapi cukup.

Akibatnya, satu-satunya saat aplikasi saya memanggil System.gc () adalah ketika akan memasuki segmen kode di mana buffer besar yang diperlukan untuk memproses data akan dialokasikan, dan pengujian pada memori bebas yang tersedia menunjukkan bahwa saya tidak dijamin memilikinya. Secara khusus, saya melihat lingkungan 1gb di mana setidaknya 300mb ditempati oleh data statis, dengan sebagian besar data non-statis terkait dengan eksekusi kecuali ketika data yang sedang diproses setidaknya berukuran 100-200 MB pada sumber. Itu semua adalah bagian dari proses konversi data otomatis, jadi semua data ada untuk jangka waktu yang relatif singkat dalam jangka panjang.

Sayangnya, meskipun informasi tentang berbagai opsi untuk menyetel pengumpul sampah tersedia, tampaknya sebagian besar merupakan proses eksperimental dan tingkat spesifik yang lebih rendah diperlukan untuk memahami cara menangani situasi khusus ini tidak mudah diperoleh.

Semua itu dikatakan, meskipun saya menggunakan System.gc (), saya masih terus menyetel menggunakan parameter baris perintah dan berhasil meningkatkan waktu pemrosesan keseluruhan aplikasi saya dengan jumlah yang relatif signifikan, meskipun tidak dapat mengatasi batu sandungan yang ditimbulkan dengan bekerja dengan blok data yang lebih besar. Karena itu, System.gc () adalah alat .... alat yang sangat tidak dapat diandalkan, dan jika Anda tidak berhati-hati dengan cara menggunakannya, Anda akan berharap alat itu tidak berfungsi lebih sering daripada tidak.

Kyune
sumber
2

Jika Anda ingin tahu apakah file System.gc() dipanggil, Anda dapat dengan Java 7 update 4 baru mendapatkan pemberitahuan ketika JVM melakukan Pengumpulan Sampah.

Saya tidak 100% yakin bahwa kelas GarbageCollectorMXBean telah diperkenalkan di Java 7 update 4, karena saya tidak dapat menemukannya di catatan rilis, tetapi saya menemukan informasinya di situs javaperformancetuning .com

Shervin Asgari
sumber
0

Saya tidak dapat memikirkan contoh spesifik saat menjalankan GC eksplisit itu baik.

Secara umum, menjalankan GC eksplisit sebenarnya dapat menyebabkan lebih banyak kerugian daripada kebaikan, karena gc eksplisit akan memicu pengumpulan penuh, yang memakan waktu lebih lama secara signifikan saat melewati setiap objek. Jika gc eksplisit ini akhirnya dipanggil berulang kali, ini dapat dengan mudah menyebabkan aplikasi menjadi lambat karena banyak waktu yang dihabiskan untuk menjalankan GC penuh.

Alternatifnya, jika menelusuri heap dengan penganalisis heap dan Anda mencurigai komponen pustaka memanggil GC eksplisit, Anda dapat menonaktifkannya dengan menambahkan: gc = -XX: + DisableExplicitGC ke parameter JVM.

zxcv
sumber
2
Kami memanggil System.gc () untuk mencoba menutup objek MappedByteBuffer kami yang sebaliknya tidak ditutup, dan oleh karena itu tidak tersedia untuk proses lain atau untuk pemotongan file, meskipun kami memanggil 'close ()' pada objek. Sayangnya masih tidak selalu menutupnya.
Tim Cooper
0

Berdasarkan Thinking in Java oleh Bruce Eckel, satu kasus penggunaan untuk panggilan System.gc () eksplisit adalah ketika Anda ingin memaksakan penyelesaian, yaitu panggilan untuk menyelesaikan metode.

Asterisk
sumber
NB: ada metode sendiri untuk memaksa proses finalisasi. (Masalahnya sebenarnya bukan pada menjalankan finalizer tetapi mengenali bahwa objek dapat diselesaikan karena tidak direferensikan)
eckes
-1

sementara system.gc bekerja, itu akan menghentikan dunia: semua tanggapan dihentikan sehingga pengumpul sampah dapat memindai setiap objek untuk memeriksa apakah perlu dihapus. jika aplikasinya adalah proyek web, semua permintaan dihentikan hingga gc selesai, dan ini akan menyebabkan proyek web Anda tidak dapat bekerja dalam satu waktu.

fleture
sumber