ניהול זיכרון (Stack vs Heap)
שני אזורי זיכרון ב-Java
Java מחלקת את הזיכרון שלה לשני אזורים עיקריים:
- Stack (מחסנית) - זיכרון "מסודר", מהיר, לנתונים זמניים
- Heap (ערימה) - זיכרון "חופשי", מנוהל על ידי ה-Garbage Collector
הבנה של ההבדל בין השניים חיונית להבנה איך Java עובדת.
Stack - המחסנית
מה נשמר שם?
- משתנים מקומיים של פונקציות
- פרמטרים של פונקציות
- ערכים פרימיטיביים (int, double, boolean וכו')
- הפניות לאובייקטים (לא האובייקטים עצמם!)
איך זה עובד?
בכל קריאה לפונקציה, Java יוצרת "מסגרת" (frame) במחסנית עם המשתנים המקומיים והפרמטרים. כשהפונקציה מסיימת - המסגרת נמחקת.
public static void main(String[] args) {
int a = 5; // a נשמר ב-stack
int b = 10; // b נשמר ב-stack
int sum = add(a, b); // נוצרת מסגרת חדשה עבור add
}
public static int add(int x, int y) {
int result = x + y; // x, y, result נשמרים ב-stack
return result;
// המסגרת נמחקת
}
מאפיינים
- מהיר - מאוד מהיר
- מוגבל - גודל קטן יחסית
- אוטומטי - ניהול אוטומטי, אין צורך לשחרר
- LIFO - Last In First Out
Heap - הערימה
מה נשמר שם?
- כל האובייקטים (כל מה שנוצר עם
new) - כל המופעים של מחלקות -
String,ArrayList, אובייקטים של מחלקות שלכם - מערכים (כן, גם מערכים פרימיטיביים)
איך זה עובד?
כשאתם כותבים new SomeClass(), האובייקט נוצר ב-heap, וההפנייה אליו נשמרת ב-stack.
public static void main(String[] args) {
Person p = new Person("דוד", 30);
// p נשמר ב-stack (הפנייה)
// האובייקט עצמו (Person) נשמר ב-heap
}
דיאגרמה מנטלית
STACK HEAP
--------- ------------------
| p ----[hpnayah]------> [Person: ]
--------- [ name="דוד" ]
[ age=30 ]
------------------
Garbage Collector
Java לא מחייבת אתכם לשחרר זיכרון ידנית. יש רכיב בשם Garbage Collector (GC) שרץ ברקע, מזהה אובייקטים שלא משתמשים בהם יותר, ומוחק אותם.
מתי אובייקט נחשב לא בשימוש? כשאין יותר אף הפנייה אליו.
Person p = new Person("דוד", 30); // נוצר
p = null; // אין הפנייה - ה-GC יאסוף אותו בסופו של דבר
מאפיינים
- איטי יותר - בגלל ה-GC ומנגנון הקצאה
- גדול - יכול להכיל הרבה יותר
- משותף - כל החוטים (threads) ניגשים אליו
דוגמה מלאה
public class MemoryDemo {
public static void main(String[] args) {
int x = 5; // stack
String name = "נפתלי"; // הפנייה ב-stack, "נפתלי" ב-heap
int[] numbers = {1, 2, 3}; // הפנייה ב-stack, המערך ב-heap
Person p = new Person("דוד", 30); // הפנייה ב-stack, האובייקט ב-heap
doSomething(x);
}
public static void doSomething(int n) {
int doubled = n * 2; // stack - ייעלם כשהפונקציה תסתיים
}
}
השלכות מעשיות
1. העברת ערכים לפונקציות
public static void modify(int x) {
x = 100; // משנה רק את העותק ב-stack המקומי
}
public static void modify(int[] arr) {
arr[0] = 100; // שינוי באובייקט ב-heap - ישפיע!
}
2. השוואת אובייקטים
String a = new String("hello");
String b = new String("hello");
a == b // false! ההפניות שונות
a.equals(b) // true - אותו תוכן
3. null
null פירושו "אין הפנייה לאובייקט". אם תנסו להשתמש בהפנייה כזו - תקבלו
NullPointerException:
String text = null;
int len = text.length(); // NullPointerException!
StackOverflowError
אם קוראים לפונקציה שקוראת לעצמה לנצח - ה-stack נגמר:
public static void endless() {
endless(); // StackOverflowError
}
זו אחת הסיבות שברקורסיה צריך תנאי עצירה.
OutOfMemoryError
אם ה-heap מתמלא (יותר מדי אובייקטים שלא נאספים):
List<int[]> huge = new ArrayList<>();
while (true) {
huge.add(new int[1_000_000]); // OutOfMemoryError
}
סיכום
| Stack | Heap |
|---|---|
| משתנים מקומיים | אובייקטים |
| פרימיטיביים | מערכים |
| הפניות | תוצאות new |
| מהיר | איטי יותר |
| אוטומטי | Garbage Collector |
| מוגבל בגודל | גדול |
כלל אצבע: פרימיטיביים ב-stack, כל דבר שנוצר עם new ב-heap.
בדקו את עצמכם
נסו לענות לבד לפני שאתם פותחים את התשובה.
-
איפה נוצר אובייקט שנוצר עם
new?הצגת התשובה
תשובה א. אובייקטים חיים ב-Heap, והמשתנה המקומי ב-Stack מחזיק רק הפניה אליהם.
-
מי משחרר זיכרון של אובייקטים שכבר לא בשימוש?
הצגת התשובה
תשובה ד. ה-GC מזהה אובייקטים שאין אליהם הפניות ומשחרר אותם אוטומטית.