מדריך ל- WeakHashMap בג'אווה

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

במאמר זה נבחן א WeakHashMap מ ה java.util חֲבִילָה.

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

במילים פשוטות, ה WeakHashMap הוא יישום מבוסס hashtable של ה- מַפָּה ממשק, עם מקשים שהם של התייחסות חלשה סוּג.

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

2. הפניות חזקות, רכות וחלשות

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

2.1. הפניות חזקות

ההתייחסות החזקה היא הסוג הנפוץ ביותר של התייחסות שאנחנו משתמשים בתכנות היומיומיות שלנו:

פריים שלם = 1;

המשתנה רִאשׁוֹנִי יש התייחסות חזקה ל מספר שלם אובייקט עם ערך 1. כל אובייקט שמפנה אליו הפניה חזקה אינו זכאי ל- GC.

2.2. הפניות רכות

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

בואו נראה איך נוכל ליצור SoftReference בג'אווה:

פריים שלם = 1; SoftReference soft = SoftReference חדש (פריים); פריים = אפס;

ה רִאשׁוֹנִי לאובייקט יש התייחסות חזקה שמצביעה עליו.

לאחר מכן, אנו עוטפים רִאשׁוֹנִי התייחסות חזקה להתייחסות רכה. לאחר התייחסות חזקה זו ריק, א רִאשׁוֹנִי אובייקט זכאי ל- GC אך ייאסף רק כאשר JVM זקוק לזיכרון לחלוטין.

2.3. הפניות חלשות

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

אנחנו יכולים ליצור התייחסות חלשה בג'אווה בצורה הבאה:

פריים שלם = 1; WeakReference רך = WeakReference חדש (ראשוני); פריים = אפס;

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

הפניות של א התייחסות חלשה סוג משמשים כמפתחות ב- WeakHashMap.

3. WeakHashMap כמטמון זיכרון יעיל

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

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

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

למרבה המזל, ה WeakHashMap יש בדיוק את המאפיינים האלה. בואו נבדוק את שלנו WeakHashMap ותראה איך זה מתנהג:

WeakHashMap map = WeakHashMap חדש (); BigImage bigImage = BigImage חדש ("image_id"); UniqueImageName imageName = חדש UniqueImageName ("name_of_big_image"); map.put (imageName, bigImage); assertTrue (map.containsKey (imageName)); imageName = null; System.gc (); להמתין (). atMost (10, TimeUnit.SECONDS). עד (map :: isEmpty);

אנחנו יוצרים WeakHashMap מקרה שיאחסן את שלנו BigImage חפצים. אנחנו שמים א BigImage אובייקט כערך ו imageName התייחסות לאובייקט כמפתח. ה imageName יאוחסן במפה כ- התייחסות חלשה סוּג.

לאחר מכן, הגדרנו את imageName התייחסות להיות ריק, לכן אין יותר אזכורים המצביעים על ה- תמונה גדולה לְהִתְנַגֵד. התנהגות ברירת המחדל של א WeakHashMap זה לתבוע מחדש ערך שלא מתייחס אליו ב- GC הבא, ולכן ערך זה יימחק מהזיכרון בתהליך GC הבא.

אנחנו קוראים a System.gc () לאלץ את JVM להפעיל תהליך GC. לאחר מחזור ה- GC, שלנו WeakHashMap יהיה ריק:

WeakHashMap map = WeakHashMap חדש (); BigImage bigImageFirst = BigImage חדש ("foo"); UniqueImageName imageNameFirst = UniqueImageName חדש ("name_of_big_image"); BigImage bigImageSecond = BigImage חדש ("foo_2"); UniqueImageName imageNameSecond = UniqueImageName חדש ("name_of_big_image_2"); map.put (imageNameFirst, bigImageFirst); map.put (imageNameSecond, bigImageSecond); assertTrue (map.containsKey (imageNameFirst)); assertTrue (map.containsKey (imageNameSecond)); imageNameFirst = null; System.gc (); לחכות (). atMost (10, TimeUnit.SECONDS). עד (() -> מפה.גודל () == 1); ממתינים (). atMost (10, TimeUnit.SECONDS) .tiltil (() -> map.containsKey (imageNameSecond));

שים לב שרק ה imageNameFirst הפניה מוגדרת ל ריק. ה imageName שניה הפניה נותרה ללא שינוי. לאחר הפעלת GC, המפה תכיל רק ערך אחד - imageName שניה.

4. מסקנה

במאמר זה בדקנו סוגי הפניות ב- Java כדי להבין כיצד java.util.WeakHashMap עובד. יצרנו מטמון פשוט הממנף התנהגות של a WeakHashMap ולבדוק אם זה עובד כמו שציפינו.

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


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