Pengumpulan Sampah Verbose di Jawa

1. Gambaran keseluruhan

Dalam tutorial ini, kita akan melihat bagaimana mengaktifkan pengumpulan sampah secara verbal dalam aplikasi Java . Kita akan mulakan dengan memperkenalkan apa itu pengumpulan sampah dan mengapa ia berguna.

Seterusnya, kita akan melihat beberapa contoh yang berbeza dan kita akan mengetahui mengenai pelbagai pilihan konfigurasi yang ada. Selain itu, kami juga akan menumpukan pada bagaimana menafsirkan output log verbose kami.

Untuk mengetahui lebih lanjut mengenai Pengumpulan Sampah (GC) dan pelbagai implementasi yang ada, lihat artikel kami mengenai Pemungut Sampah Java.

2. Pengenalan Ringkas Pengumpulan Sampah Verbose

Menghidupkan pembalakan pengumpulan sampah secara lisan sering diperlukan semasa menala dan menyahpepijat banyak masalah , terutamanya masalah ingatan. Malah, ada yang berpendapat bahawa untuk memantau kesihatan aplikasi kita dengan ketat, kita harus selalu memantau prestasi Pengumpulan Sampah JVM.

Seperti yang akan kita lihat, log GC adalah alat yang sangat penting untuk mendedahkan potensi peningkatan pada konfigurasi timbunan dan GC aplikasi kita. Untuk setiap GC yang berlaku, log GC memberikan data yang tepat mengenai hasil dan tempohnya.

Dari masa ke masa, analisis maklumat ini dapat membantu kita memahami tingkah laku aplikasi kita dengan lebih baik dan membantu kita menyesuaikan prestasi aplikasi kita. Selain itu, ia dapat membantu mengoptimumkan frekuensi GC dan masa pengumpulan dengan menentukan ukuran timbunan terbaik, pilihan JVM lain, dan algoritma GC alternatif.

2.1. Program Java Ringkas

Kami akan menggunakan program Java langsung untuk menunjukkan cara mengaktifkan dan menafsirkan log GC kami:

public class Application { private static Map stringContainer = new HashMap(); public static void main(String[] args) { System.out.println("Start of program!"); String stringWithPrefix = "stringWithPrefix"; // Load Java Heap with 3 M java.lang.String instances for (int i = 0; i < 3000000; i++) { String newString = stringWithPrefix + i; stringContainer.put(newString, newString); } System.out.println("MAP size: " + stringContainer.size()); // Explicit GC! System.gc(); // Remove 2 M out of 3 M for (int i = 0; i < 2000000; i++) { String newString = stringWithPrefix + i; stringContainer.remove(newString); } System.out.println("MAP size: " + stringContainer.size()); System.out.println("End of program!"); } }

Seperti yang dapat kita lihat dalam contoh di atas, program sederhana ini memuat 3 juta contoh String ke dalam objek Peta . Kami kemudian membuat panggilan eksplisit kepada pengumpul sampah menggunakan System.gc () .

Akhirnya, kami membuang 2 juta contoh String dari Peta . Kami juga secara eksplisit menggunakan System.out.println untuk membuat penafsiran output lebih mudah.

Di bahagian seterusnya, kita akan melihat cara mengaktifkan pembalakan GC.

3. Mengaktifkan GC Logging "sederhana"

Mari mulakan dengan menjalankan program kami dan mengaktifkan GC verbose melalui argumen permulaan JVM kami:

-XX:+UseSerialGC -Xms1024m -Xmx1024m -verbose:gc

Argumen penting di sini ialah -verbose: gc , yang mengaktifkan pembalakan maklumat pengumpulan sampah dalam bentuk termudah . Secara lalai, log GC ditulis untuk stdout dan harus menghasilkan baris untuk setiap generasi muda GC dan setiap GC penuh.

Untuk tujuan contoh kami, kami telah menentukan pengumpul sampah bersiri, pelaksanaan GC termudah, melalui argumen -XX: + UseSerialGC .

Kami juga telah menetapkan ukuran timbunan minimum dan maksimum 1024mb, tetapi tentu saja ada lebih banyak parameter JVM yang dapat kami sesuaikan.

3.1. Pemahaman Asas mengenai Hasil Kata Kerja

Sekarang mari kita lihat output program mudah kami:

Start of program! [GC (Allocation Failure) 279616K->146232K(1013632K), 0.3318607 secs] [GC (Allocation Failure) 425848K->295442K(1013632K), 0.4266943 secs] MAP size: 3000000 [Full GC (System.gc()) 434341K->368279K(1013632K), 0.5420611 secs] [GC (Allocation Failure) 647895K->368280K(1013632K), 0.0075449 secs] MAP size: 1000000 End of program!

Dalam output di atas, kita sudah dapat melihat banyak maklumat berguna mengenai apa yang sedang berlaku di dalam JVM.

Pada mulanya, keluaran ini kelihatan sangat menakutkan, tetapi sekarang mari kita lalui langkah demi langkah.

Pertama sekali, kita dapat melihat bahawa empat koleksi berlaku, satu GC Penuh dan tiga generasi Muda pembersih.

3.2. Hasil Kata Kerja dengan Lebih terperinci

Mari kita menguraikan garis output dengan lebih terperinci untuk memahami dengan tepat apa yang sedang berlaku:

  1. GC atau Full GC - Jenis Pengumpulan Sampah, sama ada GC atau Full GC untuk membezakan pengumpulan sampah kecil atau penuh
  2. (Kegagalan Alokasi) atau (System.gc ()) - Punca pengumpulan - Kegagalan Peruntukan menunjukkan bahawa tidak ada lagi ruang yang tersisa di Eden untuk memperuntukkan objek kami
  3. 279616K-> 146232K - Memori timbunan yang diduduki masing-masing sebelum dan sesudah GC (dipisahkan oleh anak panah)
  4. (1013632K) - Kapasiti timbunan semasa
  5. 0.3318607 saat - Tempoh peristiwa GC dalam beberapa saat

Oleh itu, jika kita mengambil baris pertama, 279616K-> 146232K (1013632K) bermaksud bahawa GC mengurangkan memori timbunan yang diduduki dari 279616K menjadi 146232K . Kapasiti timbunan pada masa GC adalah 1013632K , dan GC mengambil 0.3318607 saat.

Walau bagaimanapun, walaupun format pembalakan GC yang sederhana dapat berguna, ia memberikan perincian yang terhad. Sebagai contoh, kita tidak dapat mengetahui apakah GC memindahkan objek dari generasi muda ke generasi tua atau berapakah ukuran keseluruhan generasi muda sebelum dan sesudah setiap koleksi .

Untuk itu, pembalakan GC terperinci lebih berguna daripada yang mudah.

4. Mengaktifkan GC Logging "terperinci"

Untuk mengaktifkan log masuk GC terperinci, kami menggunakan argumen -XX: + PrintGCDetails . Ini akan memberi kami lebih banyak maklumat mengenai setiap GC, seperti:

  • Saiz generasi muda dan tua sebelum dan selepas setiap GC
  • Masa yang diperlukan untuk GC berlaku pada generasi muda dan tua
  • Ukuran objek yang dipromosikan di setiap GC
  • Ringkasan ukuran jumlah timbunan

Dalam contoh seterusnya, kita akan melihat cara menangkap maklumat yang lebih terperinci dalam log kita menggabungkan -verbose: gc dengan argumen tambahan ini.

Harap perhatikan bahawa bendera -XX: + PrintGCDetails sudah tidak digunakan lagi di Java 9, untuk mendukung mekanisme pembalakan bersatu yang baru (lebih lanjut mengenai ini kemudian). Bagaimanapun, setara baru -XX: + PrintGCDetails adalah pilihan -Xlog: gc * .

5. Mentafsirkan Output Kata Kerja "terperinci"

Mari jalankan lagi program contoh kami:

-XX:+UseSerialGC -Xms1024m -Xmx1024m -verbose:gc -XX:+PrintGCDetails

Kali ini outputnya agak lebih verbose:

Start of program! [GC (Allocation Failure) [DefNew: 279616K->34944K(314560K), 0.3626923 secs] 279616K->146232K(1013632K), 0.3627492 secs] [Times: user=0.33 sys=0.03, real=0.36 secs] [GC (Allocation Failure) [DefNew: 314560K->34943K(314560K), 0.4589079 secs] 425848K->295442K(1013632K), 0.4589526 secs] [Times: user=0.41 sys=0.05, real=0.46 secs] MAP size: 3000000 [Full GC (System.gc()) [Tenured: 260498K->368281K(699072K), 0.5580183 secs] 434341K->368281K(1013632K), [Metaspace: 2624K->2624K(1056768K)], 0.5580738 secs] [Times: user=0.50 sys=0.06, real=0.56 secs] [GC (Allocation Failure) [DefNew: 279616K->0K(314560K), 0.0076722 secs] 647897K->368281K(1013632K), 0.0077169 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] MAP size: 1000000 End of program! Heap def new generation total 314560K, used 100261K [0x00000000c0000000, 0x00000000d5550000, 0x00000000d5550000) eden space 279616K, 35% used [0x00000000c0000000, 0x00000000c61e9370, 0x00000000d1110000) from space 34944K, 0% used [0x00000000d3330000, 0x00000000d3330188, 0x00000000d5550000) to space 34944K, 0% used [0x00000000d1110000, 0x00000000d1110000, 0x00000000d3330000) tenured generation total 699072K, used 368281K [0x00000000d5550000, 0x0000000100000000, 0x0000000100000000) the space 699072K, 52% used [0x00000000d5550000, 0x00000000ebcf65e0, 0x00000000ebcf6600, 0x0000000100000000) Metaspace used 2637K, capacity 4486K, committed 4864K, reserved 1056768K class space used 283K, capacity 386K, committed 512K, reserved 1048576K

Kita seharusnya dapat mengenali semua elemen dari log GC yang mudah. Tetapi ada beberapa item baru.

Sekarang mari kita mempertimbangkan item baru dalam output yang diserlahkan dengan warna biru di bahagian seterusnya:

5.1. Mentafsirkan GC Minor dalam Generasi Muda

Kita akan mulakan dengan menganalisis bahagian baru dalam GC kecil:

  • [GC (Allocation Failure) [DefNew: 279616K->34944K(314560K), 0.3626923 secs] 279616K->146232K(1013632K), 0.3627492 secs] [Times: user=0.33 sys=0.03, real=0.36 secs]

As before we'll break the lines down into parts:

  1. DefNew – Name of the garbage collector used. This not so obvious name stands for the single-threaded mark-copy stop-the-world garbage collector and is what is used to clean the Young generation
  2. 279616K->34944K – Usage of the Young generation before and after collection
  3. (314560K) – The total size of the Young generation
  4. 0.3626923 secs – The duration in seconds
  5. [Times: user=0.33 sys=0.03, real=0.36 secs] – Duration of the GC event, measured in different categories

Now let's explain the different categories:

  • user – The total CPU time that was consumed by Garbage Collector
  • sys – The time spent in OS calls or waiting for system events
  • real – This is all elapsed time including time slices used by other processes

Since we're running our example using the Serial Garbage Collector, which always uses just a single thread, real-time is equal to the sum of user and system times.

5.2. Interpreting a Full GC

In this penultimate example, we see that for a major collection (Full GC), which was triggered by our system call, the collector used was Tenured.

The final piece of additional information we see is a breakdown following the same pattern for the Metaspace:

[Metaspace: 2624K->2624K(1056768K)], 0.5580738 secs]

Metaspace is a new memory space introduced in Java 8 and is an area of native memory.

5.3. Java Heap Breakdown Analysis

The final part of the output includes a breakdown of the heap including a memory footprint summary for each part of memory.

We can see that Eden space had a 35% footprint and Tenured had a 52% footprint. A summary for Metadata space and class space is also included.

From the above examples, we can now understand exactly what was happening with memory consumption inside the JVM during the GC events.

6. Adding Date and Time Information

No good log is complete without date and time information.

This extra information can be highly useful when we need to correlate GC log data with data from other sources, or it can simply help facilitate searching.

We can add the following two arguments when we run our application to get date and time information to appear in our logs:

-XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps

Each line now starts with the absolute date and time when it was written followed by a timestamp reflecting the real-time passed in seconds since the JVM started:

2018-12-11T02:55:23.518+0100: 2.601: [GC (Allocation ...

Please note that these tuning flags have been removed in Java 9. The new alternative is:

-Xlog:gc*::time

7. Logging to a File

As we've already seen, by default the GC log is written to stdout. A more practical solution is to specify an output file.

We can do this by using the argument -Xloggc: where file is the absolute path to our output file:

-Xloggc:/path/to/file/gc.log

Similar to other tuning flags, Java 9 deprecated the -Xloggc flag in favor of the new unified logging. To be more specific, now the alternative for logging to a file is:

-Xlog:gc:/path/to/file/gc.log

8. Java 9: Unified JVM Logging

As of Java 9, most of the GC related tuning flags have been deprecated in favor of the unified logging option -Xlog:gc. The verbose:gc option, however, still works in Java 9 and newer version.

For instance, as of Java 9, the equivalent of the -verbose:gc flag in the new unified logging system is:

-Xlog:gc

This will log all the info level GC logs to the standard output. It's also possible to use the -Xlog:gc= syntax to change the log level. For instance, to see all debug level logs:

-Xlog:gc=debug

As we saw earlier, we can change the output destination via the -Xlog:gc=: syntax. By default, the output is stdout, but we can change it to stderr or even a file:

-Xlog:gc=debug:file=gc.txt

Also, it's possible to add a few more fields to the output using decorators. For instance:

-Xlog:gc=debug::pid,time,uptime

Here we're printing the process id, uptime, and current timestamp in each log statement.

To see more examples of the Unified JVM Logging, see the JEP 158 standard.

9. A Tool to Analyze GC Logs

It can be time-consuming and quite tedious to analyze GC logs using a text editor. Depending on the JVM version and the GC algorithm that is used, the GC log format could differ.

Terdapat alat analisis grafik percuma yang sangat baik yang menganalisis log pengumpulan sampah, memberikan banyak metrik mengenai potensi masalah Pengumpulan Sampah, dan bahkan memberikan penyelesaian yang berpotensi untuk masalah ini.

Semestinya lihat Penganalisis Log Universal GC!

10. Kesimpulannya

Ringkasnya, dalam tutorial ini, kami telah meneroka secara terperinci pengumpulan sampah verbose di Jawa.

Pertama, kami memulakan dengan memperkenalkan pengumpulan sampah verbose dan mengapa kami mungkin mahu menggunakannya. Kami kemudian melihat beberapa contoh menggunakan aplikasi Java yang sederhana. Kami bermula dengan mengaktifkan log masuk GC dalam bentuknya yang paling mudah sebelum meneroka beberapa contoh yang lebih terperinci dan cara menafsirkan output.

Akhirnya, kami meneroka beberapa pilihan tambahan untuk log maklumat masa dan tarikh dan cara menulis maklumat ke fail log.

Contoh kod boleh didapati di GitHub.