מדריך לבריכת Java String

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

ה חוּט אובייקט הוא המחלקה הנפוצה ביותר בשפת Java.

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

2. מחרוזת אינטרינג

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

כשאנחנו יוצרים a חוּט משתנה ולהקצות לו ערך, ה- JVM מחפש את המאגר a חוּט בעל ערך שווה.

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

אם לא יימצא, הוא יתווסף לבריכה (בפנים) וההפניה שלה תוחזר.

בואו נכתוב מבחן קטן לאימות זה:

String constantString1 = "Baeldung"; String constantString2 = "Baeldung"; assertThat (constantString1) .isSameAs (constantString2);

3. מיתרים מוקצה באמצעות הבנאי

כשאנחנו יוצרים a חוּט דרך ה- חָדָשׁ מפעיל, מהדר Java ייצור אובייקט חדש וישמור אותו בשטח הערימה השמור ל- JVM.

כֹּל חוּט שנוצר כך יצביע על אזור זיכרון אחר עם כתובת משלו.

בואו נראה במה זה שונה מהמקרה הקודם:

String constantString = "Baeldung"; מחרוזת newString = מחרוזת חדשה ("Baeldung"); assertThat (constantString) .isNotSameAs (newString);

4. חוּט מילולית לעומת אובייקט מחרוזת

כשאנחנו יוצרים a חוּט אובייקט באמצעות חָדָשׁ() זה תמיד יוצר אובייקט חדש בזיכרון הערימה. מצד שני, אם ניצור אובייקט באמצעות חוּט תחביר מילולי למשל "Baeldung", הוא עשוי להחזיר אובייקט קיים ממאגר המיתרים, אם הוא כבר קיים. אחרת, הוא ייצור אובייקט מחרוזת חדש ויכניס למאגר המיתרים לשימוש חוזר בעתיד.

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

זה יהיה הרבה יותר ברור כשאנחנו משווים שניים חוּט אובייקטים שנוצרו באמצעות חוּט מילולית וה חָדָשׁ מַפעִיל:

מחרוזת ראשונה = "Baeldung"; מחרוזת שנייה = "באלדונג"; System.out.println (ראשון == שנייה); // נכון

בדוגמה זו, חוּט לאובייקטים תהיה אותה התייחסות.

לאחר מכן, בואו ניצור שני אובייקטים שונים באמצעות חָדָשׁ ובדוק שיש להם הפניות שונות:

מחרוזת שלישית = מחרוזת חדשה ("באלדונג"); מחרוזת רביעית = מחרוזת חדשה ("באלדונג"); System.out.println (שלישי == רביעי); // שקר

באופן דומה, כאשר אנו משווים א חוּט מילולית עם א חוּט אובייקט שנוצר באמצעות חָדָשׁ() באמצעות אופרטור ==, הוא יחזור שֶׁקֶר:

מחרוזת חמישית = "באלדונג"; מחרוזת שישית = מחרוזת חדשה ("באלדונג"); System.out.println (חמישי == שישי); // שקר

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

5. התמחות ידנית

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

התמחות ידנית ב- חוּט יאחסן את הפניה בבריכה, ו- JVM יחזיר את הפניה בעת הצורך.

בואו ליצור מקרה מבחן לכך:

String constantString = "Baeldung interned"; מחרוזת newString = מחרוזת חדשה ("Baeldung interned"); assertThat (constantString) .isNotSameAs (newString); מחרוזת internedString = newString.intern (); assertThat (constantString) .isSameAs (internedString);

6. איסוף זבל

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

הסיכון להתמחות מיתרים בתוך ה פרמגן (במקום ה ערימה) האם זה אנחנו יכולים להשיג OutOfMemory שְׁגִיאָה מה- JVM אם אנו מתמחים יותר מדי מיתרים.

מג'אווה 7 ואילך, בריכת המיתרים של ג'אווה היא מאוחסן ב ערימה שטח, שהוא אסוף אשפה על ידי ה- JVM. היתרון בגישה זו הוא סיכון מופחת ל OutOfMemory שְׁגִיאָה כי לא הפניה מיתרים יוסר מהבריכה ובכך ישחרר זיכרון.

7. ביצועים ואופטימיזציות

ב- Java 6, האופטימיזציה היחידה שנוכל לבצע היא הגדלת ה- פרמגן שטח במהלך קריאת התוכנית עם MaxPermSize אפשרות JVM:

-XX: MaxPermSize = 1 גרם

ב- Java 7 יש לנו אפשרויות מפורטות יותר לבחון ולהרחיב / להקטין את גודל הבריכה. בואו נראה את שתי האפשרויות לצפייה בגודל הבריכה:

-XX: + PrintFlagsFinal
-XX: + PrintStringTableStatistics

אם אנו רוצים להגדיל את גודל הבריכה מבחינת דליים, נוכל להשתמש ב- StringTableSize אפשרות JVM:

-XX: StringTableSize = 4901

לפני Java 7u40, גודל הבריכה המוגדר כברירת מחדל היה 1009 דליים אך ערך זה היה כפוף לכמה שינויים בגרסאות Java האחרונות. אם לדייק, גודל הבריכה המוגדר כברירת מחדל מג'אווה 7u40 ועד ג'אווה 11 היה 60013 ועכשיו הוא גדל ל 65536.

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

8. הערה אודות Java 9

עד Java 8, מיתרים יוצגו באופן פנימי כמערך של דמויות - לְהַשְׁחִיר[], מקודד פנימה UTF-16, כך שכל תו משתמש בשני בתים של זיכרון.

עם Java 9 ניתן ייצוג חדש, הנקרא מיתרים קומפקטיים. פורמט חדש זה יבחר את הקידוד המתאים בין לְהַשְׁחִיר[] ו בתים [] תלוי בתוכן המאוחסן.

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

9. מסקנה

במדריך זה הראינו כיצד JVM ומהדר Java מייעל את הקצאת הזיכרון עבור חוּט אובייקטים דרך מאגר המיתרים של Java.

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