מדריך System.gc ()

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

במדריך זה, אנו הולכים לחקור את System.gc () השיטה הממוקמת ב java.lang חֲבִילָה.

במפורש מתקשר System.gc () ידוע בכך שהוא נוהג רע. בואו ננסה להבין מדוע ואם יש מקרי שימוש כשקריאה לשיטה זו עשויה להיות שימושית.

2. איסוף זבל

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

  • הדור החדש (שטח קבוע) מלא, מה שמפעיל GC קל
  • הדור הישן (Eden + Survivor0 + מרחבי Survivor1) מלא, מה שמפעיל GC גדול / מלא

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

כעת, נסתכל על ה- System.gc () השיטה עצמה.

3. System.gc ()

קריאה לשיטה פשוטה:

System.gc ()

בתיעוד הרשמי של אורקל נכתב כי:

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

אין כל ערובה שה- GC בפועל יופעל.

System.gc () מפעיל GC גדול. לפיכך, קיים סיכון לבזבז זמן על שלב עצירת העולם, תלוי ביישום אספן האשפה שלך. כתוצאה, יש לנו כלי לא אמין עם עונש ביצוע משמעותי.

קיום קריאה לאיסוף אשפה מפורשת צריך להיות דגל אדום רציני לכולם.

אנחנו יכולים למנוע System.gc () מלעשות כל עבודה באמצעות -XX: DisableExplicitGC דגל JVM.

3.1. כוונון ביצועים

ראוי לציין כי רגע לפני שזורקים OutOfMemoryError, ה- JVM יבצע GC מלא. לכן, קריאה מפורשת ל System.gc () לא יציל אותנו מכישלון.

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

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

ישנן גם דרכים למתן את ההשפעות של GC מלא הנגרמות על ידי שיחה מפורשת. אנו יכולים להשתמש באחד הדגלים:

-XX: + מפורש GCInvokesConcurrent

אוֹ:

-XX: + מפורש GCInvokesConcurrentAndUnloadsClasses

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

בפרק הבא נראה דוגמה מעשית כאשר מתקשרים במפורש System.gc () נראה כי הוא שימושי.

4. דוגמא לשימוש

4.1. תַרחִישׁ

בואו נכתוב אפליקציית מבחן. אנו רוצים למצוא מצב בעת התקשרות System.gc () עשוי להיות שימושי.

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

בואו נדמיין שיש לנו אוסף עצום של חפצים שחיים מזה זמן מה. ואז, בשלב מסוים, אנו מנקים את אוסף החפצים. אולי זה רגע טוב לרוץ System.gc ()?

4.2. יישום הדגמה

ניצור אפליקציית קונסולה פשוטה שתאפשר לנו לדמות תרחיש זה:

מחלקה ציבורית DemoApplication {מטמון מפת גמר פרטי פרטי = HashMap חדש (); סטטי ציבורי ריק ריק (String [] args) {Scanner scanner = new Scanner (System.in); בעוד (scanner.hasNext ()) {final String הבא = scanner.next (); אם ("מילוי". שווה (הבא)) {עבור (int i = 0; i <1000000; i ++) {cache.put (randomUUID (). toString (), randomUUID (). toString ()); }} אחרת אם ("לפסול" .equals (הבא)) {cache.clear (); } אחר אם ("gc" .equals (הבא)) {System.gc (); } אחר אם ("יציאה". שווה (הבא)) {System.exit (0); } אחר {System.out.println ("לא ידוע"); }}}}

4.3. מריץ את ההדגמה

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

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

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

ראשית, בואו נסה למלא מקום קבוע. סוּג למלא.

אנחנו יכולים לחקור את שלנו gclog.log קובץ כדי לראות מה קרה. נראה כ -15 אוספים. השורה שנרשמה לאוספים בודדים נראית כך:

197.057: [GC (כשל בהקצאה) 197.057: [ParNew: 67498K-> 40K (75840K), 0.0016945 שניות] 168754K-> 101295K (244192K), 0.0017865 שניות] [Times: user = 0.01 sys = 0.00, real = 0.00 secs] שניות]

כפי שאנו רואים, הזיכרון מתמלא.

הבא, בואו כּוֹחַ System.gc () על ידי הקלדה gc. אנו יכולים לראות כי השימוש בזיכרון לא השתנה באופן משמעותי:

238.810: [GC מלא (System.gc ()) 238.810: [CMS: 101255K-> 101231K (168352K); 0.2634318 שניות] 120693K-> 101231K (244192K), [Metaspace: 32186K-> 32186K (1079296K)], 0.2635908 שניות] [Times: user = 0.27 sys = 0.00, real = 0.26 secs]

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

בואו נקה את המטמון על ידי הקלדה לִפְסוֹל. אנחנו לא צריכים לראות שורות יומן נוספות מופיעות ב- gclog.log קוֹבֶץ.

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

262.124: [GC מלא (System.gc ()) 262.124: [CMS: 101523K-> 14122K (169324K); 0.0975656 שניות] 103369K-> 14122K (245612K), [Metaspace: 32203K-> 32203K (1079296K)], 0.0977279 שניות] [Times: user = 0.10 sys = 0.00, real = 0.10 secs]

הוצאנו כמות מרשימה של זיכרון! אך האם היה זה באמת הכרחי כרגע? מה קרה?

על פי דוגמה זו, התקשרות System.gc () אולי נראה מפתה כאשר אנו משחררים חפצים גדולים או מבטלים מטמונים.

5. שימושים אחרים

יש מעט מאוד סיבות כאשר שיחה מפורשת אל System.gc () שיטה עשויה להיות שימושית.

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

אחר הוא ניתוח דליפת זיכרוןזה יותר נוהלי ניפוי באגים מאשר משהו שנרצה לשמור בקוד הייצור. יִעוּד System.gc () וראיית שטח הערימה עדיין גבוהה עשויה להוות אינדיקציה לדליפת זיכרון.

6. סיכום

במאמר זה חקרנו את System.gc () ומתי זה עשוי להיראות שימושי.

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

כרגיל, ניתן למצוא את הקוד המשמש במאמר זה ב- GitHub.


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