מרחב ההשקה של ג'אווה נגד המחסנית – הקצאת זיכרון בג'אווה

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

Java Heap Space

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

Java Stack Memory

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

זיכרון הגומלין וזיכרון המחסנאות בתוכנית Java

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

package com.journaldev.test;

public class Memory {

	public static void main(String[] args) { // Line 1
		int i=1; // Line 2
		Object obj = new Object(); // Line 3
		Memory mem = new Memory(); // Line 4
		mem.foo(obj); // Line 5
	} // Line 9

	private void foo(Object param) { // Line 6
		String str = param.toString(); //// Line 7
		System.out.println(str);
	} // Line 8

}

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

  • מיד לאחר הפעלת התוכנית, היא מטעינה את כל המחלקות של זמן ריצה לתוך זיכרון הגומלין. כאשר מתקבלת השיטה main() בשורה 1, זמן ריצה של Java יוצר זיכרון מחסנאות לשימוש על ידי תהליך השיטה main().
  • אנו יוצרים משתנה מקומי פשוט בשורה 2, כך שהוא נוצר ומאוחסן בזיכרון המחסנית של שיטת main().
  • מכיוון שאנו יוצרים אובייקט בשורה השלישית, הוא נוצר בזיכרון הheap והזיכרון הstack מכיל את ההפניה אליו. תהליך דומה קורה כאשר אנו יוצרים אובייקט בזיכרון הheap בשורה הרביעית.
  • כעת, כשאנו קוראים לשיטת foo() בשורה החמישית, נוצר קטע בראש המחסנית לשימוש של שיטת foo(). מכיוון ש-Java היא "עבור על פי ערך", נוצרת הפניה חדשה לאובייקט בבלוק ה-mחסנית של foo() בשורה השישית.
  • A string is created in the 7th line, it goes in the String Pool in the heap space and a reference is created in the foo() stack space for it.
  • שיטת foo() נסגרת בשורה השמינית, בעת זו הבלוק שהוקצה ל-foo() ב-mחסנית נפנה.
  • בשורה 9, שיטת main() סיימה והזיכרון המחסנית שנוצר עבור שיטת main() נהרס. גם התוכנית נגמרת בשורה זו, ולכן Java Runtime משחררת את כל הזיכרון ומסיימת את ביצוע התוכנית.

ההבחנה בין זיכרון ה-Heap וזיכרון ה-Stack של Java

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

  1. זיכרון ה-Heap משמש על ידי כל חלקי היישום בעוד שזיכרון ה-Stack משמש רק על ידי תהליכי אחד של ביצוע.
  2. כל פעם שנוצרת עצם, הוא תמיד נשמר בחלל ההשקה וזיכרון הערימה מכיל את הייחוס אליו. זיכרון הערימה מכיל רק משתנים פרימיטיביים מקומיים ומשתנים המצביעים לעצמים בחלל ההשקה.
  3. העצמים שמאוחסנים בחלל ההשקה נגישים באופן גלובלי בעוד שזיכרון הערימה אינו נגיש על ידי תהליכים אחרים.
  4. ניהול זיכרון בערימה נעשה בדרך LIFO, בעוד שהוא יותר מורכב בזיכרון ההשקה משום שהוא משמש באופן גלובלי. זיכרון ההשקה מחולק לקבוצות כמו "Young-Generation", "Old-Generation" וכדומה, פרטים נוספים ב- איסוף אשפה ב-Java.
  5. זיכרון הערימה הוא קצר החיים והוא קיים מההתחלה ועד סיום ביצועי היישום.
  6. ניתן להשתמש ב--Xms ו--Xmx אפשרויות ה- JVM כדי להגדיר את גודל ההתחלה והגודל המרבי של זיכרון ההשקה. ניתן להשתמש ב--Xss כדי להגדיר גודל זיכרון הערימה.
  7. כאשר זיכרון הערימה מלא, זמן הרצת Java זורק java.lang.StackOverFlowError, בעוד שאם זיכרון ההשקה מלא, הוא זורק שגיאת java.lang.OutOfMemoryError: Java Heap Space.
  8. גודל זיכרון הערימה הוא נמוך מאוד בהשוואה לזיכרון ההשקה. בגלל פשטות בהקצאת הזיכרון (LIFO), זיכרון הערימה הוא מהיר מאוד בהשוואה לזיכרון ההשקה.

זהו כל המידע על Java Heap Space vs Stack Memory בהקשר של יישום Java, אני מקווה שזה יסייע לך להבין את תהליכי ההקצאת זיכרון כאשר תכנית Java כלשהי מופעלת.

הפנייה: https://en.wikipedia.org/wiki/Java_memory_model.

Source:
https://www.digitalocean.com/community/tutorials/java-heap-space-vs-stack-memory