Mengapa praktik buruk memanggil System.gc ()?

326

Setelah menjawab pertanyaan tentang bagaimana cara memaksa benda bebas di Jawa (orang itu membersihkan HashMap 1.5GB) dengan System.gc(), saya diberitahu itu adalah praktik buruk untuk memanggil System.gc()secara manual, tetapi komentar itu tidak sepenuhnya meyakinkan. Selain itu, sepertinya tidak ada yang berani untuk menjawab, atau menurunkan jawaban saya.

Saya diberitahu di sana bahwa ini adalah praktik yang buruk, tetapi kemudian saya juga diberitahu bahwa menjalankan pemulung tidak secara sistematis menghentikan dunia lagi, dan bahwa itu juga dapat secara efektif digunakan oleh JVM hanya sebagai petunjuk, jadi saya agak bingung

Saya mengerti bahwa JVM biasanya lebih tahu dari Anda ketika perlu merebut kembali memori. Saya juga mengerti bahwa mengkhawatirkan beberapa kilobyte data itu konyol. Saya juga mengerti bahwa bahkan megabyte data tidak seperti beberapa tahun yang lalu. Tapi tetap saja, 1,5 gigabytes? Dan Anda tahu ada sekitar 1,5 GB data yang berkeliaran di memori; itu tidak seperti tembakan dalam gelap. Apakah System.gc()secara sistematis buruk, atau adakah titik di mana itu menjadi baik?

Jadi pertanyaannya sebenarnya ganda:

  • Mengapa atau tidak itu praktik buruk untuk menelepon System.gc()? Apakah ini benar-benar hanya petunjuk bagi JVM di bawah implementasi tertentu, atau apakah itu selalu merupakan siklus pengumpulan penuh? Adakah benar-benar implementasi pengumpul sampah yang dapat melakukan pekerjaan mereka tanpa menghentikan dunia? Tolong jelaskan berbagai pernyataan yang dibuat orang dalam komentar atas jawaban saya .
  • Di mana ambangnya? Apakah tidak pernah merupakan ide yang baik untuk menelepon System.gc(), atau adakah saat-saat itu dapat diterima? Jika demikian, jam berapa itu?
zneak
sumber
4
Saya pikir waktu yang tepat untuk memanggil System.gc () adalah ketika Anda melakukan sesuatu yang sudah lama memuat proses. Misalnya saya sedang mengerjakan game dan berencana untuk memanggil System.gc () saat game memuat level baru, di akhir proses pemuatan. Pengguna sudah menunggu sedikit, dan peningkatan kinerja ekstra mungkin sepadan; tapi saya juga akan menempatkan opsi di layar konfigurasi untuk menonaktifkan perilaku ini.
Ricket
41
Bozho: perhatikan kata pertama dari pertanyaan itu. MENGAPA itu praktik terbaik untuk tidak menyebutnya? Mengulang-ulang mantra tidak menjelaskan apa-apa.
HANYA SAYA PENDAPAT benar
1
Kasus lain telah dijelaskan di java-monitor.com/forum/showthread.php?t=188 Di mana ia menjelaskan bagaimana bisa menjadi praktik yang buruk untuk memanggil System.gc ()
Shirishkumar Bari

Jawaban:

243

Alasan setiap orang selalu mengatakan untuk menghindari System.gc()adalah bahwa itu adalah indikator yang cukup bagus dari kode yang rusak secara fundamental . Setiap kode yang bergantung padanya untuk kebenaran pasti rusak; apa pun yang mengandalkannya untuk kinerja kemungkinan besar rusak.

Anda tidak tahu pemulung seperti apa yang Anda jalankan. Tentu saja ada beberapa yang tidak "menghentikan dunia" seperti yang Anda tegaskan, tetapi beberapa JVM tidak sepintar itu atau karena berbagai alasan (mungkin mereka menggunakan telepon?) Jangan lakukan itu. Anda tidak tahu apa yang harus dilakukan.

Juga, tidak dijamin melakukan apa pun. JVM mungkin sepenuhnya mengabaikan permintaan Anda.

Kombinasi dari "Anda tidak tahu apa yang akan dilakukan," "Anda tidak tahu apakah itu akan membantu," dan "Anda tidak perlu menyebutnya demikian" adalah mengapa orang begitu kuat mengatakan bahwa secara umum Anda seharusnya tidak menyebutnya. Saya pikir ini adalah kasus "jika Anda perlu bertanya apakah Anda harus menggunakan ini, Anda tidak boleh"


EDIT untuk mengatasi beberapa masalah dari utas lainnya:

Setelah membaca utas yang Anda tautkan, ada beberapa hal lagi yang ingin saya tunjukkan. Pertama, seseorang menyarankan bahwa panggilan gc()dapat mengembalikan memori ke sistem. Itu tentu tidak selalu benar - tumpukan Jawa itu sendiri tumbuh secara independen dari alokasi Jawa.

Seperti pada, JVM akan menyimpan memori (puluhan megabyte) dan menumbuhkan tumpukan yang diperlukan. Itu tidak selalu mengembalikan memori itu ke sistem bahkan ketika Anda membebaskan objek Java; sangat bebas untuk menggunakan memori yang dialokasikan untuk digunakan untuk alokasi Java di masa depan.

Untuk menunjukkan bahwa mungkin System.gc()tidak melakukan apa-apa, lihat:

http://bugs.sun.com/view_bug.do?bug_id=6668279

dan khususnya bahwa ada opsi -XX: DisableExplicitGC VM.

Steven Schlansker
sumber
1
Anda mungkin dapat membangun beberapa pengaturan Rube Goldberg-esque yang aneh di mana metode di mana GC dijalankan mempengaruhi kebenaran kode Anda. Mungkin menutupi beberapa interaksi threading yang aneh, atau mungkin finalizer memiliki efek yang signifikan terhadap jalannya program. Saya tidak sepenuhnya yakin itu mungkin tapi itu mungkin, jadi saya pikir saya akan menyebutkannya.
Steven Schlansker
2
@ zneak, misalnya, Anda mungkin telah memasukkan kode kritis ke finalizers (yang merupakan kode yang pada dasarnya rusak)
Martin
3
Saya ingin menambahkan bahwa ada beberapa kasus sudut di mana System.gc()berguna dan bahkan mungkin diperlukan. Sebagai contoh dalam aplikasi UI pada Windows, ia dapat mempercepat proses pemulihan Window ketika Anda memanggil System.gc () sebelum Anda meminimalkan Window (terutama ketika tetap diminimalkan untuk beberapa waktu dan beberapa bagian dari proses ditukar dengan disk).
Joachim Sauer
2
@AndrewJanke Saya akan mengatakan kode yang menggunakan WeakReferences untuk objek yang ingin Anda pegang salah sejak awal, pengumpulan sampah atau tidak. Anda akan memiliki masalah yang sama dengan C ++ dengan std::weak_ptr(meskipun Anda mungkin melihat masalah dalam versi C ++ lebih awal daripada Anda dalam versi Java karena penghancuran objek tidak akan ditunda seperti biasanya biasanya finalisasi).
JAB
2
@ Rebeccah Itu bug, jadi ya, saya akan menyebutnya 'pasti rusak'. Fakta yang System.gc()memperbaikinya adalah solusi, bukan praktik pengkodean yang baik.
Steven Schlansker
149

Telah dijelaskan bahwa panggilan system.gc() mungkin tidak melakukan apa-apa, dan bahwa setiap kode yang "perlu" dijalankan oleh pengumpul sampah rusak.

Namun, alasan pragmatis bahwa menelepon System.gc()adalah praktik yang buruk adalah karena tidak efisien. Dan dalam kasus terburuk, itu sangat tidak efisien ! Biarkan saya jelaskan.

Algoritma GC khas mengidentifikasi sampah dengan melintasi semua objek non-sampah di heap, dan menyimpulkan bahwa objek apa pun yang tidak dikunjungi pasti sampah. Dari sini, kita dapat memodelkan kerja total pengumpulan sampah yang terdiri dari satu bagian yang sebanding dengan jumlah data langsung, dan bagian lain yang sebanding dengan jumlah sampah; yaitu work = (live * W1 + garbage * W2).

Sekarang anggaplah Anda melakukan hal berikut dalam aplikasi single-threaded.

System.gc(); System.gc();

Panggilan pertama akan (kami perkirakan) (live * W1 + garbage * W2)berfungsi, dan menyingkirkan sampah yang beredar.

Panggilan kedua akan (live* W1 + 0 * W2)bekerja dan tidak mendapatkan kembali apa pun. Dengan kata lain kita telah melakukan (live * W1)pekerjaan dan sama sekali tidak mencapai apa pun .

Kita dapat memodelkan efisiensi pengumpul sebagai jumlah pekerjaan yang dibutuhkan untuk mengumpulkan satu unit sampah; yaitu efficiency = (live * W1 + garbage * W2) / garbage. Jadi untuk membuat GC seefisien mungkin, kita perlu memaksimalkan nilai garbageketika kita menjalankan GC; yaitu menunggu sampai tumpukan penuh. (Dan juga, buat heap sebesar mungkin. Tapi itu adalah topik yang terpisah.)

Jika aplikasi tidak mengganggu (dengan memanggil System.gc()), GC akan menunggu sampai tumpukan penuh sebelum berjalan, menghasilkan pengumpulan sampah yang efisien 1 . Tetapi jika aplikasi memaksa GC untuk menjalankan, kemungkinan bahwa tumpukan tidak akan penuh, dan hasilnya adalah sampah dikumpulkan secara tidak efisien. Dan semakin sering aplikasi memaksa GC, semakin tidak efisien GC menjadi.

Catatan: penjelasan di atas menjelaskan fakta bahwa GC modern yang khas mem-partisi heap menjadi "spasi", GC mungkin secara dinamis memperluas heap, kumpulan aplikasi objek non-sampah aplikasi dapat bervariasi dan seterusnya. Meski begitu, prinsip dasar yang sama berlaku di seluruh papan untuk semua pengumpul sampah sejati 2 . Tidak efisien untuk memaksa GC menjalankan.


1 - Beginilah cara kerja kolektor "throughput". Pengumpul bersamaan seperti CMS dan G1 menggunakan kriteria yang berbeda untuk memutuskan kapan memulai pengumpul sampah.

2 - Saya juga mengecualikan manajer memori yang menggunakan penghitungan referensi secara eksklusif, tetapi tidak ada implementasi Java saat ini menggunakan pendekatan itu ... untuk alasan yang baik.

Stephen C
sumber
45
+1 Penjelasan yang bagus. Namun perlu dicatat bahwa alasan ini hanya berlaku jika Anda peduli dengan throughput. Jika Anda ingin mengoptimalkan laten pada titik tertentu, pemaksaan GC mungkin masuk akal. Misalnya (secara hipotesis) dalam permainan Anda mungkin ingin menghindari keterlambatan selama level, tetapi Anda tidak peduli tentang keterlambatan saat level load. Maka masuk akal untuk memaksa GC setelah beban level. Memang mengurangi throughput keseluruhan, tapi bukan itu yang Anda optimalkan.
sleske
2
@sleske - apa yang Anda katakan itu benar. Namun, menjalankan GC antar level adalah solusi bantuan-band ... dan tidak menyelesaikan masalah latensi jika level tersebut membutuhkan waktu yang cukup lama sehingga Anda perlu menjalankan GC selama level tersebut. Pendekatan yang lebih baik adalah dengan menggunakan pengumpul sampah bersamaan (jeda rendah) ... jika platform mendukungnya.
Stephen C
Tidak sepenuhnya yakin apa yang Anda maksud dengan "membutuhkan pengumpul sampah untuk menjalankan". Persyaratan aplikasi yang harus dihindari adalah; kegagalan alokasi yang tidak pernah dapat dipenuhi dan overhead GC yang tinggi. Melakukan panggilan secara acak ke System.gc () seringkali menghasilkan overhead GC yang tinggi.
Kirk
@Kirk - "Secara acak melakukan panggilan ke System.gc () seringkali menghasilkan overhead GC yang tinggi." . Saya tahu itu. Begitu juga siapa pun yang telah membaca dan memahami Jawaban saya.
Stephen C
@Kirk - "Tidak sepenuhnya yakin apa yang Anda maksud ..." - Maksud saya program di mana perilaku "benar" dari suatu program tergantung pada GC yang berjalan pada waktu tertentu; misalnya untuk mengeksekusi finalizer, atau untuk memecahkan WeakReferences atau SoftReferences. Ini ide yang sangat buruk untuk melakukan ini ... tapi ini yang saya bicarakan. Lihat juga jawaban Steven Schlansker.
Stephen C
47

Banyak orang tampaknya mengatakan kepada Anda untuk tidak melakukan ini. Saya tidak setuju. Jika, setelah proses pemuatan besar seperti memuat level, Anda yakin bahwa:

  1. Anda memiliki banyak objek yang tidak dapat dijangkau dan mungkin belum ditemukan. dan
  2. Anda pikir pengguna dapat bertahan dengan sedikit penurunan pada saat ini

tidak ada salahnya memanggil System.gc (). Saya melihatnya seperti inlinekata kunci c / c ++ . Ini hanya petunjuk kepada gc bahwa Anda, pengembang, telah memutuskan bahwa waktu / kinerja tidak sepenting biasanya dan bahwa sebagian dari itu dapat digunakan memori reklamasi.

Saran untuk tidak bergantung pada melakukan sesuatu adalah benar. Jangan mengandalkan itu bekerja, tetapi memberi petunjuk bahwa sekarang adalah waktu yang dapat diterima untuk mengumpulkan itu baik-baik saja. Saya lebih suka membuang waktu pada suatu titik dalam kode di mana tidak masalah (memuat layar) daripada ketika pengguna secara aktif berinteraksi dengan program (seperti saat level permainan).

Ada satu waktu ketika saya akan memaksa pengumpulan: ketika mencoba untuk mencari tahu adalah kebocoran objek tertentu (baik kode asli atau interaksi panggilan balik yang besar dan kompleks. Oh dan komponen UI yang sangat mirip dengan Matlab.) Ini tidak boleh digunakan dalam kode produksi.

KitsuneYMG
sumber
3
+1 untuk GC sambil menganalisis untuk kebocoran mem. Perhatikan bahwa informasi tentang penggunaan heap (Runtime.freeMemory () et al.) Benar-benar hanya bermakna setelah memaksa GC, jika tidak maka akan tergantung pada kapan sistem terakhir repot untuk menjalankan GC.
sleske
4
tidak ada salahnya memanggil System.gc () mungkin memerlukan stop the worldpendekatan dan ini benar-benar berbahaya, jika terjadi
bestsss
7
Ada adalah salahnya memanggil pengumpul sampah secara eksplisit. Memanggil GC pada waktu yang salah akan menghabiskan siklus CPU. Anda (programmer) tidak memiliki informasi yang cukup untuk menentukan kapan waktu yang tepat adalah ... tetapi JVM melakukannya.
Stephen C
Jetty membuat 2 panggilan ke System.gc () setelah startup dan saya jarang melihatnya membuat perbedaan.
Kirk
Yah pengembang Jetty memiliki bug. Itu akan membuat perbedaan ... bahkan jika perbedaan itu sulit dikuantifikasi.
Stephen C
31

Orang-orang telah melakukan pekerjaan dengan baik menjelaskan mengapa TIDAK untuk digunakan, jadi saya akan memberi tahu Anda beberapa situasi di mana Anda harus menggunakannya:

(Komentar berikut berlaku untuk Hotspot yang berjalan di Linux dengan kolektor CMS, di mana saya merasa yakin mengatakan bahwa System.gc()sebenarnya selalu meminta pengumpulan sampah penuh).

  1. Setelah pekerjaan awal untuk memulai aplikasi Anda, Anda mungkin memiliki kondisi penggunaan memori yang buruk. Setengah generasi masa jabatan Anda bisa penuh dengan sampah, yang berarti Anda jauh lebih dekat dengan CMS pertama Anda. Dalam aplikasi yang penting, bukan ide buruk untuk memanggil System.gc () untuk "mengatur ulang" tumpukan Anda ke status awal data langsung.

  2. Sepanjang baris yang sama dengan # 1, jika Anda memantau penggunaan tumpukan Anda dari dekat, Anda ingin memiliki pembacaan yang akurat tentang apa penggunaan memori dasar Anda. Jika 2 menit pertama uptime aplikasi Anda adalah semua inisialisasi, data Anda akan kacau kecuali Anda memaksakan (ahem ... "sarankan") gc penuh di muka.

  3. Anda mungkin memiliki aplikasi yang dirancang untuk tidak pernah mempromosikan apa pun kepada generasi bertenor saat sedang berjalan. Tetapi mungkin Anda perlu menginisialisasi beberapa data di muka yang tidak terlalu besar untuk secara otomatis dipindahkan ke generasi yang bertenor. Kecuali jika Anda memanggil System.gc () setelah semuanya diatur, data Anda dapat disimpan di generasi baru hingga saatnya tiba untuk dipromosikan. Tiba-tiba aplikasi super-duper low-latency, low-GC Anda terkena hukuman latensi BESAR (relatif berbicara, tentu saja) untuk mempromosikan objek-objek tersebut selama operasi normal.

  4. Kadang-kadang berguna untuk memiliki panggilan System.gc tersedia dalam aplikasi produksi untuk memverifikasi keberadaan kebocoran memori. Jika Anda tahu bahwa kumpulan data langsung pada waktu X harus ada dalam rasio tertentu terhadap kumpulan data langsung pada waktu Y, maka mungkin berguna untuk memanggil System.gc () waktu X dan waktu Y dan membandingkan penggunaan memori .

JT.
sumber
1
Untuk sebagian besar pengumpul sampah generasi, objek dalam generasi baru harus bertahan hidup dalam jumlah tertentu (seringkali dapat dikonfigurasikan) dari pengumpulan sampah, jadi memanggil System.gc()satu kali untuk memaksa promosi benda tidak menguntungkan Anda. Dan Anda tentu tidak ingin menelepon System.gc()delapan kali berturut-turut dan berdoa semoga sekarang, promosi telah dilakukan dan penghematan biaya promosi nanti membenarkan biaya beberapa GC penuh. Bergantung pada algoritma GC, mempromosikan banyak objek bahkan mungkin tidak menanggung biaya yang sebenarnya, karena hanya akan menugaskan kembali memori ke generasi lama atau menyalin secara bersamaan ...
Holger
11

Ini adalah pertanyaan yang sangat menyusahkan, dan saya merasa berkontribusi pada banyak orang yang menentang Jawa meskipun betapa berharganya suatu bahasa.

Fakta bahwa Anda tidak dapat mempercayai "System.gc" untuk melakukan apa pun adalah hal yang sangat menakutkan dan dapat dengan mudah memanggil "Ketakutan, Ketidakpastian, Keraguan" terasa dalam bahasa tersebut.

Dalam banyak kasus, senang menangani lonjakan memori yang Anda sebabkan dengan sengaja sebelum peristiwa penting terjadi, yang akan menyebabkan pengguna menganggap program Anda dirancang / tidak responsif.

Memiliki kemampuan untuk mengendalikan pengumpulan sampah akan menjadi alat pendidikan yang hebat, pada gilirannya meningkatkan pemahaman masyarakat tentang cara pengumpulan sampah dan bagaimana membuat program mengeksploitasi perilaku defaultnya serta perilaku terkontrol.

Biarkan saya meninjau argumen utas ini.

  1. Itu tidak efisien:

Seringkali, program mungkin tidak melakukan apa-apa dan Anda tahu itu tidak melakukan apa-apa karena cara itu dirancang. Misalnya, itu mungkin melakukan semacam penantian panjang dengan kotak pesan tunggu yang besar, dan pada akhirnya mungkin juga menambah panggilan untuk mengumpulkan sampah karena waktu untuk menjalankannya akan mengambil sebagian kecil dari waktu lama menunggu tetapi akan menghindari gc dari bertingkah di tengah operasi yang lebih penting.

  1. Itu selalu merupakan praktik yang buruk dan menunjukkan kode yang rusak.

Saya tidak setuju, tidak masalah apa pun yang Anda miliki. Tugasnya adalah melacak sampah dan membersihkannya.

Dengan memanggil gc pada saat penggunaannya kurang kritis, Anda mengurangi peluangnya berjalan ketika hidup Anda bergantung pada kode tertentu yang sedang dijalankan tetapi sebaliknya ia memutuskan untuk mengumpulkan sampah.

Tentu, itu mungkin tidak berperilaku seperti yang Anda inginkan atau harapkan, tetapi ketika Anda ingin menyebutnya, Anda tahu tidak ada yang terjadi, dan pengguna bersedia mentolerir kelambatan / downtime. Jika System.gc berfungsi, bagus! Jika tidak, setidaknya Anda sudah mencoba. Tidak ada sisi negatifnya kecuali pengumpul sampah memiliki efek samping yang melekat yang melakukan sesuatu yang sangat tidak terduga terhadap bagaimana seorang pengumpul sampah seharusnya berperilaku jika diminta secara manual, dan ini dengan sendirinya menyebabkan ketidakpercayaan.

  1. Ini bukan kasus penggunaan umum:

Ini adalah use case yang tidak dapat dicapai dengan andal, tetapi bisa jadi jika sistem dirancang seperti itu. Ini seperti membuat lampu lalu lintas dan membuatnya sehingga beberapa / semua tombol lampu lalu lintas tidak melakukan apa-apa, itu membuat Anda mempertanyakan mengapa tombol itu ada untuk memulai, javascript tidak memiliki fungsi pengumpulan sampah sehingga kami tidak akan meneliti sebanyak itu untuk itu.

  1. Spesifikasi mengatakan bahwa System.gc () adalah petunjuk bahwa GC harus dijalankan dan VM bebas untuk mengabaikannya.

apa itu "petunjuk"? apa itu "abaikan"? komputer tidak bisa sekadar mengambil petunjuk atau mengabaikan sesuatu, ada jalur perilaku ketat yang diperlukan yang mungkin dinamis yang dipandu oleh maksud sistem. Jawaban yang tepat akan mencakup apa yang sebenarnya dilakukan oleh pemulung, pada tingkat implementasi, yang menyebabkannya tidak melakukan pengumpulan ketika Anda memintanya. Apakah fitur ini hanya sebuah kegagalan? Adakah kondisi yang harus saya penuhi? Apa saja kondisi ini?

Seperti namanya, Java GC sering tampak seperti monster yang tidak Anda percayai. Anda tidak tahu kapan itu akan datang atau pergi, Anda tidak tahu apa yang akan dilakukan, bagaimana itu akan dilakukan. Saya dapat membayangkan beberapa ahli memiliki gagasan yang lebih baik tentang bagaimana Koleksi Sampah mereka bekerja berdasarkan per-instruksi, tetapi sebagian besar hanya berharap itu "hanya bekerja", dan harus mempercayai algoritma yang tampaknya buram untuk melakukan pekerjaan untuk Anda membuat frustrasi.

Ada kesenjangan besar antara membaca tentang sesuatu atau diajarkan sesuatu, dan benar-benar melihat implementasinya, perbedaan antar sistem, dan mampu bermain dengannya tanpa harus melihat kode sumber. Ini menciptakan kepercayaan diri dan perasaan penguasaan / pemahaman / kontrol.

Untuk meringkas, ada masalah yang melekat dengan jawaban "fitur ini mungkin tidak melakukan apa-apa, dan saya tidak akan menjelaskan bagaimana cara mengetahui kapan ia melakukan sesuatu dan kapan tidak dan mengapa itu tidak atau akan, sering menyiratkan bahwa itu bertentangan dengan filosofi untuk mencoba melakukannya, bahkan jika maksud di baliknya masuk akal ".

Mungkin baik bagi Java GC untuk berperilaku seperti itu, atau mungkin tidak, tetapi untuk memahaminya, sulit untuk benar-benar mengikuti arah mana yang harus ditempuh untuk mendapatkan gambaran komprehensif tentang apa yang dapat Anda percayai untuk dilakukan oleh GC dan tidak melakukannya, jadi terlalu mudah untuk tidak mempercayai bahasa, karena tujuan dari suatu bahasa adalah untuk mengendalikan perilaku hingga batas filosofis (mudah bagi seorang programmer, terutama pemula untuk masuk ke dalam krisis eksistensial dari perilaku sistem / bahasa tertentu) Anda mampu mentolerir (dan jika Anda tidak bisa, Anda tidak akan menggunakan bahasa sampai Anda harus), dan lebih banyak hal yang Anda tidak bisa kendalikan tanpa alasan yang diketahui mengapa Anda tidak bisa mengendalikannya secara inheren berbahaya.

Dmitry
sumber
9

Efisiensi GC bergantung pada sejumlah heuristik. Sebagai contoh, heuristik umum adalah bahwa akses tulis ke objek biasanya terjadi pada objek yang dibuat belum lama ini. Lainnya adalah bahwa banyak objek berumur pendek (beberapa objek akan digunakan untuk waktu yang lama, tetapi banyak yang akan dibuang beberapa mikrodetik setelah pembuatannya).

Memanggil System.gc()seperti menendang GC. Ini berarti: "semua parameter yang disetel dengan hati-hati, organisasi pintar itu, semua upaya yang Anda lakukan untuk mengalokasikan dan mengelola objek sedemikian rupa sehingga segalanya berjalan lancar, well, letakkan seluruh lot, dan mulai dari awal". Ini dapat meningkatkan kinerja, tetapi sebagian besar waktu hanya menurunkan kinerja.

Untuk menggunakan dengan System.gc()andal (*) Anda perlu tahu bagaimana GC beroperasi di semua detailnya. Detail seperti itu cenderung berubah sedikit jika Anda menggunakan JVM dari vendor lain, atau versi berikutnya dari vendor yang sama, atau JVM yang sama tetapi dengan opsi baris perintah yang sedikit berbeda. Jadi itu jarang merupakan ide yang bagus, kecuali jika Anda ingin mengatasi masalah tertentu di mana Anda mengontrol semua parameter tersebut. Oleh karena itu gagasan "praktik buruk": itu tidak dilarang, metodenya ada, tetapi jarang membuahkan hasil.

(*) Saya berbicara tentang efisiensi di sini. System.gc()tidak akan pernah merusak program Java yang benar. Ini tidak akan membuat memori tambahan bahwa JVM tidak bisa mendapatkan sebaliknya: sebelum melempar OutOfMemoryError, JVM melakukan pekerjaan System.gc(), bahkan jika sebagai pilihan terakhir.

Thomas Pornin
sumber
1
+1 untuk menyebutkan System.gc () tidak mencegah OutOfMemoryError. Beberapa orang percaya ini.
sleske
1
Sebenarnya, ini dapat mencegah OutOfMemoryError karena penanganan referensi lunak. SoftReferences yang dibuat setelah proses GC terakhir tidak dikumpulkan dalam implementasi yang saya tahu. Tetapi ini adalah detail implementasi yang dapat berubah kapan saja dan semacam bug dan tidak ada yang harus Anda andalkan.
maaartinus
8

Kadang-kadang ( tidak sering! ) Anda benar-benar tahu lebih banyak tentang penggunaan memori masa lalu, saat ini dan masa depan daripada waktu menjalankan. Ini tidak terlalu sering terjadi, dan saya akan mengklaim tidak pernah dalam aplikasi web saat halaman normal sedang dilayani.

Beberapa tahun yang lalu saya mengerjakan generator laporan, itu

  • Punya satu utas
  • Baca "permintaan laporan" dari antrian
  • Memuat data yang diperlukan untuk laporan dari database
  • Membuat laporan dan mengirimkannya melalui email.
  • Diulang selamanya, tidur ketika tidak ada permintaan luar biasa.
  • Itu tidak menggunakan kembali data antara laporan dan tidak melakukan pencairan.

Pertama karena itu bukan waktu nyata dan pengguna diharapkan untuk menunggu laporan, penundaan sementara menjalankan GC bukan masalah, tapi kami perlu menghasilkan laporan pada tingkat yang lebih cepat daripada yang diminta.

Melihat garis besar proses di atas, jelaslah itu.

  • Kami tahu akan ada sangat sedikit objek langsung setelah laporan dikirimkan melalui email, karena permintaan berikutnya belum mulai diproses.
  • Diketahui bahwa biaya menjalankan siklus pengumpulan sampah tergantung pada jumlah objek hidup, jumlah sampah memiliki sedikit pengaruh pada biaya menjalankan GC.
  • Bahwa ketika antrian kosong tidak ada yang lebih baik untuk dilakukan kemudian jalankan GC.

Oleh karena itu jelas itu layak saat menjalankan GC setiap kali antrian permintaan kosong; tidak ada kerugian untuk ini.

Mungkin perlu menjalankan GC setelah setiap laporan diemail, karena kami tahu ini adalah waktu yang tepat untuk menjalankan GC. Namun jika komputer memiliki ram yang cukup, hasil yang lebih baik akan diperoleh dengan menunda menjalankan GC.

Perilaku ini dikonfigurasikan pada basis per instalasi, untuk beberapa pelanggan yang mengaktifkan GC paksa setelah setiap laporan mempercepat perlindungan laporan. (Saya berharap ini karena memori rendah pada server mereka dan itu menjalankan banyak proses lainnya, jadi karena itu waktu yang baik memaksa paging GC berkurang.)

Kami tidak pernah mendeteksi instalasi yang tidak menguntungkan adalah menjalankan GC paksa setiap kali antrian pekerjaan kosong.

Tapi, biar jelas, hal di atas bukanlah kasus yang umum.

Ian Ringrose
sumber
3

Mungkin saya menulis kode jelek, tetapi saya menyadari bahwa mengklik ikon trash-can pada gerhana dan netbeans IDE adalah 'praktik yang baik'.

Ryan Fernandes
sumber
1
Ini mungkin begitu. Tetapi jika Eclipse atau NetBeans diprogram untuk menelepon System.gc()secara berkala, Anda mungkin akan menganggap perilaku itu mengganggu.
Stephen C
2

Pertama, ada perbedaan antara spesifikasi dan kenyataan. Spesifikasi mengatakan bahwa System.gc () adalah petunjuk bahwa GC harus dijalankan dan VM bebas untuk mengabaikannya. Kenyataannya adalah, VM tidak akan pernah mengabaikan panggilan ke System.gc ().

Memanggil GC hadir dengan overhead non-sepele untuk panggilan tersebut dan jika Anda melakukan ini pada waktu yang acak, kemungkinan Anda tidak akan melihat imbalan atas usaha Anda. Di sisi lain, koleksi yang dipicu secara alami sangat mungkin untuk menutup biaya panggilan. Jika Anda memiliki informasi yang menunjukkan bahwa GC harus dijalankan daripada Anda dapat membuat panggilan ke System.gc () dan Anda akan melihat manfaatnya. Namun, ini pengalaman saya bahwa ini hanya terjadi dalam beberapa kasus tepi karena sangat tidak mungkin Anda memiliki informasi yang cukup untuk memahami apakah dan kapan System.gc () harus dipanggil.

Salah satu contoh yang tercantum di sini, mengenai tong sampah di IDE Anda. Jika Anda pergi ke sebuah rapat, mengapa tidak melakukannya. Overhead tidak akan mempengaruhi Anda dan tumpukan mungkin dibersihkan ketika Anda kembali. Lakukan ini dalam sistem produksi dan panggilan untuk mengumpulkan yang sering akan menghentikannya! Bahkan panggilan sesekali seperti yang dilakukan oleh RMI dapat mengganggu kinerja.

Gereja
sumber
3
"Kenyataannya adalah, VM tidak akan pernah mengabaikan panggilan ke System.gc ()." - salah Baca tentang -XX: -DisableExplicitGC.
Stephen C
1

Ya, memanggil System.gc () tidak menjamin itu akan berjalan, itu permintaan ke JVM yang mungkin diabaikan. Dari dokumen:

Memanggil metode gc menunjukkan bahwa Java Virtual Machine mengeluarkan upaya untuk mendaur ulang objek yang tidak digunakan

Itu hampir selalu ide yang buruk untuk menyebutnya karena manajemen memori otomatis biasanya lebih tahu daripada Anda kapan harus gc. Ini akan melakukannya ketika kumpulan memori bebas internal rendah, atau jika OS meminta sebagian memori dikembalikan.

Mungkin dapat diterima untuk memanggil System.gc () jika Anda tahu itu membantu. Maksud saya, Anda telah menguji dan mengukur perilaku kedua skenario pada platform penyebaran secara menyeluruh , dan Anda dapat menunjukkan bahwa itu membantu. Berhati-hatilah karena gc tidak mudah diprediksi - ini bisa membantu pada satu kali lari dan melukai yang lain.

tom
sumber
<stroke> Tetapi juga dari Javadoc: _Ketika kontrol kembali dari pemanggilan metode, mesin virtual telah melakukan upaya terbaik untuk mendaur ulang semua objek yang dibuang, yang saya lihat sebagai bentuk yang lebih penting dari apa yang Anda posting. </ stroke > Persetan, ada laporan bug yang menyesatkan. Seperti yang lebih tahu, apa salahnya mengisyaratkan JVM?
zneak
1
Bahayanya adalah bahwa melakukan pengumpulan pada waktu yang salah bisa sangat melambat. Petunjuk yang Anda berikan mungkin salah. Adapun komentar "upaya terbaik", coba dan lihat di alat seperti JConsole. Terkadang mengklik tombol "Lakukan GC" tidak menghasilkan apa
tom
Maaf tidak setuju tetapi memanggil System.gc () di OpenJDK dan apa pun yang didasarkan padanya (HP misalnya) selalu menghasilkan siklus pengumpulan sampah. Bahkan yang tampaknya berlaku untuk implementasi J9 IBM juga
Kirk
@Kirk - Salah: google, dan membaca tentang -XX: -DisableExplicitGC.
Stephen C
0

Dalam pengalaman saya, menggunakan System.gc () secara efektif merupakan bentuk optimasi platform-spesifik (di mana "platform" adalah kombinasi arsitektur perangkat keras, OS, versi JVM dan kemungkinan parameter runtime yang lebih seperti RAM yang tersedia), karena perilakunya, sementara kira-kira dapat diprediksi pada platform tertentu, dapat (dan akan) bervariasi antar platform.

Ya, ada yang situasi di mana System.gc () akan meningkatkan (dianggap) kinerja. Contohnya adalah jika penundaan dapat ditoleransi di beberapa bagian aplikasi Anda, tetapi tidak di bagian lain (contoh game yang dikutip di atas, di mana Anda ingin GC terjadi pada awal level, bukan pada level).

Namun, apakah itu akan membantu atau menyakiti (atau tidak melakukan apa-apa) sangat tergantung pada platform (sebagaimana didefinisikan di atas).

Jadi saya pikir ini valid sebagai optimalisasi platform-resort khusus terakhir (yaitu jika optimasi kinerja lainnya tidak cukup). Tetapi Anda tidak boleh menyebutnya hanya karena Anda percaya itu mungkin membantu (tanpa tolok ukur tertentu), karena kemungkinan besar tidak akan melakukannya.

sleske
sumber
0
  1. Karena objek dialokasikan secara dinamis dengan menggunakan operator baru,
    Anda mungkin bertanya-tanya bagaimana objek tersebut dihancurkan dan
    memori mereka dilepaskan untuk realokasi nanti.

  2. Dalam beberapa bahasa, seperti C ++, objek yang dialokasikan secara dinamis harus dilepaskan secara manual dengan menggunakan operator hapus.

  3. Java mengambil pendekatan yang berbeda; ini menangani deallokasi untuk Anda secara otomatis.
  4. Teknik yang mencapai ini disebut pengumpulan sampah. Ia bekerja seperti ini: ketika tidak ada referensi ke suatu objek, objek itu dianggap tidak lagi diperlukan, dan memori yang ditempati oleh objek dapat direklamasi. Tidak ada kebutuhan eksplisit untuk menghancurkan objek seperti di C ++.
  5. Pengumpulan sampah hanya terjadi secara sporadis (jika ada) selama pelaksanaan program Anda.
  6. Itu tidak akan terjadi hanya karena ada satu atau lebih objek yang tidak lagi digunakan.
  7. Selain itu, implementasi run-time Java yang berbeda akan mengambil berbagai pendekatan untuk pengumpulan sampah, tetapi untuk sebagian besar, Anda tidak harus memikirkannya saat menulis program Anda.
Tanmay Delhikar
sumber