אוסף זבל רבתי בג'אווה

1. סקירה כללית

במדריך זה, נבדוק כיצד להפעיל אוסף זבל רבתי ביישום Java. נתחיל בהכרת מה זה אוסף זבל רבתי ומדוע זה יכול להיות שימושי.

לאחר מכן, נבחן מספר דוגמאות שונות ונלמד על אפשרויות התצורה השונות הקיימות. בנוסף, אנו נתמקד גם כיצד לפרש את תפוקת יומנינו הרבים.

למידע נוסף על אוסף האשפה (GC) ועל היישומים השונים הקיימים, עיין במאמר שלנו בנושא Java Garbage Collectors.

2. מבוא קצר לאיסוף זבל רבתי

לעתים קרובות נדרש להפעיל רישום אוסף אשפה ורב-פעולות בעת כוונון וניקוי באגים בנושאים רבים, במיוחד בעיות זיכרון. למעשה, יש הטוענים שכדי לעקוב בקפדנות אחר בריאות היישומים שלנו, עלינו תמיד לפקח על ביצועי איסוף הזבל של JVM.

כפי שנראה, יומן ה- GC הוא כלי חשוב מאוד לחשיפת שיפורים פוטנציאליים בתצורת הערימה וה- GC של היישום שלנו. עבור כל GC שקורה, יומן GC מספק נתונים מדויקים לגבי תוצאותיו ומשך הזמן.

לאורך זמן, ניתוח מידע זה יכול לעזור לנו להבין טוב יותר את התנהגות היישום שלנו ולעזור לנו לכוון את ביצועי היישום שלנו. יתר על כך, זה יכול לעזור לייעל את תדירות ה- GC ואת זמני האיסוף על ידי ציון גדלי הערימה הטובים ביותר, אפשרויות JVM אחרות ואלגוריתמים חלופיים של GC.

2.1. תוכנית Java פשוטה

נשתמש בתוכנית Java פשוטה כדי להדגים כיצד לאפשר ולפרש את יומני ה- GC שלנו:

יישום בכיתה ציבורית {string stringContainer מפה פרטית = HashMap חדש (); ראשי סטטי ציבורי ריק (String [] args) {System.out.println ("התחלת התוכנית!"); String stringWithPrefix = "stringWithPrefix"; // טען את Java Heap עם 3 M מופעי java.lang.String עבור (int i = 0; i <3000000; i ++) {String newString = stringWithPrefix + i; stringContainer.put (newString, newString); } System.out.println ("גודל MAP:" + stringContainer.size ()); // GC מפורש! System.gc (); // הסר 2 M מתוך 3 M עבור (int i = 0; i <2000000; i ++) {String newString = stringWithPrefix + i; stringContainer.remove (newString); } System.out.println ("גודל MAP:" + stringContainer.size ()); System.out.println ("סוף התוכנית!"); }}

כפי שניתן לראות בדוגמה שלעיל, תוכנית פשוטה זו טוענת 3 מיליון חוּט מקרים לתוך א מַפָּה לְהִתְנַגֵד. לאחר מכן אנו מבצעים שיחה מפורשת לאוסף הזבל באמצעות System.gc ().

לבסוף, אנו מסירים 2 מיליון מההון חוּט מקרים מה מַפָּה. אנו גם משתמשים במפורש System.out.println כדי להקל על פרשנות התפוקה.

בחלק הבא נראה כיצד להפעיל רישום GC.

3. הפעלת רישום GC "פשוט"

נתחיל בהפעלת התוכנית שלנו ומאפשרת GC מילולית באמצעות טיעוני ההפעלה של JVM:

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

הטיעון החשוב כאן הוא ה מילולית: gc, שמפעיל רישום מידע על איסוף אשפה בצורתו הפשוטה ביותר. כברירת מחדל, יומן ה- GC נכתב ל- stdout וצריך להוציא שורה לכל GC צעיר דור ולכל GC מלא.

למטרות הדוגמה שלנו, ציינו באמצעות הארגומנט את אספן האשפה הסדרתי, היישום הפשוט ביותר של GC -XX: + UseSerialGC.

הגדרנו גם גודל ערימה מינימלי ומקסימלי של 1024mb, אך יש, כמובן, יותר פרמטרים של JVM שנוכל לכוון.

3.1. הבנה בסיסית של התפוקה המילולית

בואו נסתכל על תפוקת התוכנית הפשוטה שלנו:

תחילת התוכנית! [GC (הקצאת הקצאה) 279616K-> 146232K (1013632K), 0.3318607 שניות] [GC (הקצאת הקצאה) 425848K-> 295442K (1013632K), 0.4266943 שניות] גודל MAP: 3000000 [GC מלא (System.gc ()) 434341K- > 368279K (1013632K), 0.5420611 שניות] [GC (הקצאת הקצאה) 647895K-> 368280K (1013632K), 0.0075449 שניות] גודל MAP: 1000000 סוף התוכנית!

בפלט שלעיל אנו רואים כבר מידע רב שימושי על הנעשה בתוך ה- JVM.

בהתחלה, הפלט הזה יכול להראות די מרתיע, אבל בואו נעבור אותו שלב אחר שלב.

ראשית כל, אנו רואים שארבעה אוספים התקיימו, GC מלא ושלושה דורות צעירים מנקים.

3.2. התפוקה המילולית בפירוט רב יותר

בואו נפרק את קווי הפלט ביתר פירוט כדי להבין בדיוק מה קורה:

  1. GC אוֹ GC מלאגם סוג אוסף האשפה GC אוֹ GC מלא כדי להבחין באוסף אשפה קטין או מלא
  2. (כישלון הקצאה) אוֹ (System.gc ()) - סיבת האוסף - כשל בהקצאה מצביע על כך שלא נותר עוד מקום בעדן להקצות את החפצים שלנו
  3. 279616K-> 146232K - זיכרון הערימה הכבושה לפני ואחרי GC, בהתאמה (מופרד על ידי חץ)
  4. (1013632K) - הקיבולת הנוכחית של הערימה
  5. 0.3318607 שניות - משך אירוע ה- GC בשניות

לפיכך, אם ניקח את השורה הראשונה, 279616K-> 146232K (1013632K) פירושו שה- GC הפחית את זיכרון הערימה הכבושה מ- 279616K ל 146232K. יכולת הערימה בזמן GC הייתה 1013632K, וה- GC לקח 0.3318607 שניות.

עם זאת, למרות שפורמט הרישום של GC פשוט יכול להיות שימושי, הוא מספק פרטים מוגבלים. לדוגמא, איננו יכולים לדעת אם ה- GC העביר אובייקטים מהדור הצעיר לדור הישן או מה היה הגודל הכולל של הדור הצעיר לפני ואחרי כל אוסף..

מסיבה זו, רישום GC מפורט יותר שימושי מזה הפשוט.

4. הפעלת רישום GC "מפורט"

כדי להפעיל את רישום ה- GC המפורט, אנו משתמשים בטיעון -XX: + PrintGCDetails. זה ייתן לנו פרטים נוספים על כל GC, כגון:

  • גודל הדור הצעיר והזקן לפני ואחרי כל GC
  • הזמן שלוקח GC לקרות בדור צעיר ומבוגר
  • גודל האובייקטים שמקודמים בכל GC
  • סיכום גודל הערמה הכוללת

בדוגמה הבאה נראה כיצד ניתן ללכוד מידע מפורט עוד יותר ביומנינו בשילוב מילולית: gc עם הטיעון הנוסף הזה.

שים לב כי -XX: + PrintGCDetails הדגל הוצא משימוש ב- Java 9, לטובת מנגנון הרישום המאוחד החדש (עוד על כך בהמשך). בכל מקרה, המקבילה החדשה של -XX: + PrintGCDetails האם ה -Xlog: gc * אוֹפְּצִיָה.

5. פירוש התפוקה הפירושית "המפורטת"

בואו נפעיל את תוכנית הדוגמה שלנו שוב:

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

הפעם התפוקה די מילולית יותר:

תחילת התוכנית! [GC (הקצאת הקצאה) [DefNew: 279616K-> 34944K (314560K), 0.3626923 שניות] 279616K-> 146232K (1013632K), 0.3627492 שניות] [Times: user = 0.33 sys = 0.03, real = 0.36 secs] [GC (Allocation כשל) [DefNew: 314560K-> 34943K (314560K), 0.4589079 שניות] 425848K-> 295442K (1013632K), 0.4589526 שניות] [Times: user = 0.41 sys = 0.05, real = 0.46 secs] גודל MAP: 3000000 [GC מלא ( System.gc ()) [קבוע: 260498K-> 368281K (699072K), 0.5580183 שניות] 434341K-> 368281K (1013632K), [Metaspace: 2624K-> 2624K (1056768K)], 0.5580738 שניות] [Times: user = 0.50 sys = 0.06, אמיתי = 0.56 שניות] [GC (כשל בהקצאה) [DefNew: 279616K-> 0K (314560K), 0.0076722 שניות] 647897K-> 368281K (1013632K), 0.0077169 שניות] [Times: user = 0.01 sys = 0.00, real = 0.01 שניות] גודל MAP: 1000000 סוף התוכנית! Heap def סה"כ דור חדש סך הכל 314560K, בשימוש 100261K [0x00000000c0000000, 0x00000000d5550000, 0x00000000d5550000) עדן שטח 279616K, 35% בשימוש [0x00000000c0000000, 0x00000000c61e9370, 0x00000000d1110000) מהחלל 34944K, 0% בשימוש משומש [0x00000000d1110000, 0x00000000d1110000, 0x00000000d3330000) סה"כ דור קבוע 699072K, משומש 368281K [0x00000000d5550000, 0x0000000100000000, 0x000000010000000000) המרחב 699072K, 52% משמש [0x00000000d5550000, 0x00000000c0000, 6600 שטח כיתה בשימוש 283K, קיבולת 386K, התחייב 512K, שמור 1048576K

אנו אמורים להיות מסוגלים לזהות את כל האלמנטים מתוך יומן GC הפשוט. אבל ישנם מספר פריטים חדשים.

בואו ניקח בחשבון את הפריטים החדשים בפלט שמודגשים בכחול בחלק הבא:

5.1. פירוש GC קטן בדור הצעיר

נתחיל בניתוח החלקים החדשים ב- GC מינורי:

  • [GC (כשל בהקצאה) [DefNew: 279616K-> 34944K (314560K), 0.3626923 שניות] 279616K-> 146232K (1013632K), 0.3627492 שניות] [Times: user = 0.33 sys = 0.03, real = 0.36 secs]

כמו קודם נפרק את השורות לחלקים:

  1. DefNew - שם אספן האשפה בו נעשה שימוש. השם הלא כל כך מובן מאליו זה מייצג את אספן האשפה עם הברגה אחת עם הברגה חד-הברגה והוא מה שמשמש לניקוי הדור הצעיר
  2. 279616K-> 34944K - שימוש בדור הצעיר לפני ואחרי האיסוף
  3. (314560K) הגודל הכולל של הדור הצעיר
  4. 0.3626923 שניות - משך השניות
  5. [זמנים: משתמש = 0.33 sys = 0.03, real = 0.36 שניות] - משך אירוע ה- GC, נמדד בקטגוריות שונות

עכשיו נסביר את הקטגוריות השונות:

  • מִשׁתַמֵשׁ - זמן המעבד הכולל שנצרך על ידי אספן האשפה
  • sys - הזמן המושקע בשיחות מערכת ההפעלה או בהמתנה לאירועי מערכת
  • אמיתי - זה כל הזמן שחלף כולל פרוסות זמן המשמשות תהליכים אחרים

מכיוון שאנחנו מריצים את הדוגמה שלנו באמצעות אספן הזבל הסידורי, שמשתמש תמיד רק בשרשור יחיד, זמן אמת שווה לסכום זמני המשתמשים והמערכת.

5.2. פירוש GC מלא

בדוגמה הלפני אחרונה זו אנו רואים כי עבור אוסף מרכזי (Full GC), שהופעל על ידי קריאת המערכת שלנו, היה האספן שהשתמשנו בו קבוע.

החלק הנוסף של מידע נוסף שאנו רואים הוא פירוט לפי אותו דפוס עבור ה- מטאספייס:

[Metaspace: 2624K-> 2624K (1056768K)], 0.5580738 שניות]

מטאספייס הוא שטח זיכרון חדש שהוצג בג'אווה 8 והוא אזור של זיכרון מקורי.

5.3. ניתוח התמוטטות של Java Heap

החלק האחרון של הפלט כולל פירוט של הערימה כולל סיכום טביעת רגל זיכרון לכל חלק בזיכרון.

אנו יכולים לראות כי שטח עדן היה בעל טביעת רגל של 35% ולטנורד היה טביעת רגל של 52%. סיכום עבור מרחב מטא-נתונים ומרחב כיתות כלול גם כן.

מהדוגמאות לעיל, כעת אנו יכולים להבין בדיוק מה קרה עם צריכת זיכרון בתוך ה- JVM במהלך אירועי ה- GC.

6. הוספת מידע על תאריך ושעה

אין יומן טוב שלם ללא מידע על תאריך ושעה.

מידע נוסף זה יכול להיות שימושי ביותר כשאנחנו צריכים לתאם נתוני יומן GC עם נתונים ממקורות אחרים, או שהוא פשוט יכול להקל על החיפוש.

אנו יכולים להוסיף את שני הטיעונים הבאים כאשר אנו מריצים את היישום שלנו כדי לקבל מידע על תאריך ושעה שיופיע ביומנינו:

-XX: + PrintGCTimeStamps -XX: + PrintGCDateStamps

כל שורה מתחילה כעת בתאריך ובשעה המוחלטים שבהם הוא נכתב ואחריו חותמת זמן המשקפת את הזמן האמיתי שעבר בשניות מאז תחילת ה- JVM:

2018-12-11T02: 55: 23.518 + 0100: 2.601: [GC (הקצאה ...

לידיעתך, דגלי כוונון אלה הוסרו ב- Java 9. החלופה החדשה היא:

-Xlog: gc * :: זמן

7. כניסה לקובץ

כפי שכבר ראינו, כברירת מחדל יומן ה- GC נכתב stdout. פתרון מעשי יותר הוא ציון קובץ פלט.

אנו יכולים לעשות זאת באמצעות הטיעון -Xloggc: איפה קוֹבֶץ הוא הנתיב המוחלט לקובץ הפלט שלנו:

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

בדומה לדגלי כוונון אחרים, ג'אווה 9 ביטל את דגל -Xloggc לטובת הרישום המאוחד החדש. ליתר דיוק, כעת האלטרנטיבה לכניסה לקובץ היא:

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

8. Java 9: ​​רישום JVM מאוחד

נכון ל- Java 9, מרבית דגלי הכוונון הקשורים ל- GC הוצאו משימוש לטובת אפשרות הרישום המאוחדת -Xlog: gc. ה מילולית: gc עם זאת, האופציה עדיין פועלת בג'אווה 9 ובגרסה חדשה יותר.

לדוגמה, נכון ל- Java 9, המקבילה ל- מילולית: gc הדגל במערכת הרישום החדשה המאוחדת הוא:

-Xlog: gc

זה יתעד את כל יומני GC ברמת המידע לפלט הסטנדרטי. אפשר גם להשתמש ב- -Xlog: gc = תחביר לשינוי רמת היומן. למשל, כדי לראות את כל יומני רמת הבאגים:

-Xlog: gc = ניפוי באגים

כפי שראינו קודם, אנו יכולים לשנות את יעד הפלט באמצעות ה- -Xlog: gc =: תחביר. כברירת מחדל, ה- תְפוּקָה הוא stdout, אבל אנחנו יכולים לשנות את זה ל סטדרר או אפילו קובץ:

-Xlog: gc = ניפוי: file = gc.txt

כמו כן, ניתן להוסיף עוד כמה שדות לפלט באמצעות קישוטים. לדוגמה:

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

כאן אנו מדפיסים את מזהה התהליך, זמן הפעולה וחותמת הזמן הנוכחית בכל משפט יומן.

כדי לראות דוגמאות נוספות לרישום JVM המאוחד, עיין בתקן JEP 158.

9. א כלי לניתוח יומני GC

זה יכול להיות זמן רב ודי מייגע לנתח יומני GC באמצעות עורך טקסט. תלוי בגרסת JVM ובאלגוריתם GC המשמש, פורמט יומן ה- GC עשוי להיות שונה.

יש כלי ניתוח גרפי בחינם טוב מאוד המנתח את יומני איסוף האשפה, מספק מדדים רבים אודות בעיות פוטנציאליות של איסוף אשפה, ואף מספק פתרונות פוטנציאליים לבעיות אלה.

בהחלט לבדוק את מנתח יומני ה- GC האוניברסלי!

10. מסקנה

לסיכום, במדריך זה חקרנו בפירוט אוסף זבל רבתי בג'אווה.

ראשית, התחלנו להציג מהו אוסף זבל רבתי ומדוע נרצה להשתמש בו. לאחר מכן בדקנו כמה דוגמאות באמצעות יישום Java פשוט. התחלנו עם הפעלת רישום GC בצורה הפשוטה ביותר לפני שבדקנו כמה דוגמאות מפורטות יותר וכיצד לפרש את הפלט.

לבסוף בחנו מספר אפשרויות נוספות לרישום מידע על זמן ותאריך וכיצד לכתוב מידע לקובץ יומן רישום.

ניתן למצוא את דוגמאות הקוד ב- GitHub.


$config[zx-auto] not found$config[zx-overlay] not found