Panduan Sistem.gc ()

1. Gambaran keseluruhan

Dalam tutorial ini, kita akan menyiasat kaedah System.gc () yang terdapat dalam pakej java.lang .

Secara terang-terangan memanggil System.gc () dikenali sebagai amalan yang tidak baik. Mari cuba memahami mengapa dan jika ada kes penggunaan ketika memanggil kaedah ini mungkin berguna.

2. Pengumpulan Sampah

Mesin Maya Java memutuskan untuk melakukan pengumpulan sampah apabila terdapat petunjuk untuk melakukannya. Petunjuk tersebut berbeza antara satu pelaksanaan GC dengan yang lain. Mereka berdasarkan heuristik yang berbeza. Namun, ada beberapa saat GC akan dilaksanakan dengan pasti:

  • Generasi baru (ruang Tenured) penuh, yang mencetuskan GC kecil
  • Generasi lama (ruang Eden + Survivor0 + Survivor1) penuh, yang mencetuskan GC utama / penuh

Satu-satunya perkara yang bebas dari pelaksanaan GC adalah kelayakan objek untuk dikumpulkan sampah.

Sekarang, kita akan melihat kaedah System.gc () itu sendiri.

3. Sistem.gc ()

Permintaan kaedahnya mudah:

System.gc()

Dokumentasi rasmi Oracle menyatakan bahawa:

Memanggil kaedah gc menunjukkan bahawa Java Virtual Machine berusaha untuk mengitar semula objek yang tidak digunakan untuk membuat memori yang mereka tempati sekarang tersedia untuk digunakan kembali dengan cepat.

Tidak ada jaminan bahawa GC sebenar akan dicetuskan .

System.gc () mencetuskan GC utama. Oleh itu, ada risiko menghabiskan masa pada fasa berhenti-dunia, bergantung pada pelaksanaan pengutip sampah anda. Hasilnya, kami mempunyai alat yang tidak boleh dipercayai dengan hukuman prestasi yang berpotensi signifikan .

Adanya pemungutan sampah yang eksplisit mesti menjadi bendera merah yang serius bagi semua orang.

Kita boleh menghalang System.gc () melakukan sebarang pekerjaan dengan menggunakan bendera -XX: DisableExplicitGC JVM.

3.1. Penalaan Prestasi

Perlu diingat bahawa sebelum membuang OutOfMemoryError, JVM akan melakukan GC penuh. Oleh itu, panggilan eksplisit ke System.gc () tidak akan menyelamatkan kita dari kegagalan .

Pengutip sampah pada masa kini benar-benar pintar. Mereka mempunyai semua pengetahuan tentang penggunaan memori dan statistik lain untuk dapat membuat keputusan yang tepat. Oleh itu, kita harus mempercayai mereka.

Sekiranya terdapat masalah memori, kami mempunyai banyak tetapan yang dapat kami ubah untuk menyesuaikan aplikasi kami - mulai dari memilih pengumpul sampah yang berbeda, dengan menetapkan nisbah waktu aplikasi / waktu GC yang diinginkan, dan akhirnya, diakhiri dengan menetapkan ukuran tetap untuk segmen memori.

Terdapat juga cara untuk mengurangkan kesan GC Penuh yang disebabkan oleh panggilan eksplisit. Kita boleh menggunakan salah satu bendera:

-XX:+ExplicitGCInvokesConcurrent

atau:

-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses

Sekiranya kita benar-benar mahu aplikasi kita berfungsi dengan baik, kita harus menyelesaikan masalah memori asas yang sebenarnya.

Pada bab seterusnya, kita akan melihat contoh praktikal ketika memanggil System.gc () secara jelas nampaknya berguna.

4. Contoh Penggunaan

4.1. Senario

Mari tulis aplikasi ujian. Kami ingin mencari keadaan ketika memanggil System.gc () mungkin berguna .

Pengumpulan sampah kecil berlaku lebih kerap daripada yang utama. Jadi, kita mungkin harus fokus pada yang terakhir. Satu objek dipindahkan ke ruang tenur jika ia "bertahan" beberapa koleksi dan masih dapat dicapai dari akar GC.

Cuba bayangkan kita mempunyai koleksi objek yang banyak yang masih hidup untuk beberapa waktu. Kemudian, pada satu ketika, kami membersihkan koleksi objek. Mungkin ini adalah saat yang baik untuk menjalankan System.gc () ?

4.2. Permohonan Demo

Kami akan membuat aplikasi konsol mudah yang membolehkan kami mensimulasikan senario itu:

public class DemoApplication { private static final Map cache = new HashMap(); public static void main(String[] args) { Scanner scanner = new Scanner(System.in); while (scanner.hasNext()) { final String next = scanner.next(); if ("fill".equals(next)) { for (int i = 0; i < 1000000; i++) { cache.put(randomUUID().toString(), randomUUID().toString()); } } else if ("invalidate".equals(next)) { cache.clear(); } else if ("gc".equals(next)) { System.gc(); } else if ("exit".equals(next)) { System.exit(0); } else { System.out.println("unknown"); } } } }

4.3. Menjalankan Demo

Mari jalankan aplikasi kami dengan beberapa bendera tambahan:

-XX:+PrintGCDetails -Xloggc:gclog.log -Xms100M -Xmx500M -XX:+UseConcMarkSweepGC

Dua bendera pertama diperlukan untuk mencatat maklumat GC. Dua bendera seterusnya menetapkan ukuran timbunan awal dan kemudian ukuran timbunan maksimum. Kami mahu mengekalkan saiz timbunan yang rendah untuk memaksa GC menjadi lebih aktif. Akhirnya, kami memutuskan untuk menggunakan CMS - Pengumpul sampah Tanda Bersama dan Sapu. Sudah tiba masanya untuk menjalankan aplikasi kami!

Mula-mula, mari kita cuba mengisi ruang jangka masa . Taipkan isian.

Kami boleh menyiasat fail gclog.log kami untuk melihat apa yang berlaku. Kami akan melihat sekitar 15 koleksi. Garis yang dicatat untuk koleksi tunggal kelihatan seperti:

197.057: [GC (Allocation Failure) 197.057: [ParNew: 67498K->40K(75840K), 0.0016945 secs] 168754K->101295K(244192K), 0.0017865 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] secs]

Seperti yang dapat kita lihat, memori itu terisi.

Seterusnya, mari kita memaksa System.gc () dengan menaip gc . Kita dapat melihat penggunaan memori tidak berubah dengan ketara:

238.810: [Full GC (System.gc()) 238.810: [CMS: 101255K->101231K(168352K); 0.2634318 secs] 120693K->101231K(244192K), [Metaspace: 32186K->32186K(1079296K)], 0.2635908 secs] [Times: user=0.27 sys=0.00, real=0.26 secs]

After a few more runs, we'll see that memory size stays at the same level.

Let's clear the cache by typing invalidate. We should see no more log lines appear in the gclog.log file.

We can try to fill cache a few more times, but no GC is happening. This is a moment when we can outsmart the garbage collector. Now, after forcing GC, we'll see a line like:

262.124: [Full GC (System.gc()) 262.124: [CMS: 101523K->14122K(169324K); 0.0975656 secs] 103369K->14122K(245612K), [Metaspace: 32203K->32203K(1079296K)], 0.0977279 secs] [Times: user=0.10 sys=0.00, real=0.10 secs]

We've released an impressive amount of memory! But was it really necessary right now? What happened?

According to this example, calling System.gc() might seem tempting when we're releasing big objects or invalidating caches.

5. Other Usages

There are very few reasons when an explicit call to the System.gc() method might be useful.

One possible reason is cleaning memory after server startup — we're starting a server or application which does a lot of preparation. After that, there are a lot of objects to be finalized. However, cleaning after such preparation shouldn't be our responsibility.

Another is memory leak analysisit's more a debugging practice than something we would like to keep in the production code. Calling System.gc() and seeing heap space still being high might be an indication of a memory leak.

6. Summary

In this article, we investigated the System.gc() method and when it might seem useful.

Kita tidak boleh bergantung padanya mengenai kebenaran aplikasi kita. Dalam kebanyakan kes, GC lebih pintar daripada kami, dan sekiranya terdapat masalah ingatan, kami harus mempertimbangkan untuk menala mesin maya dan bukannya membuat panggilan eksplisit.

Seperti biasa, kod yang digunakan dalam artikel ini boleh didapati di GitHub.