目錄
01-2. Process 的組成(Code, Data, Stack, Heap)
⏱️ 閱讀時間: 10 分鐘 🎯 難度: ⭐⭐ (簡單)
🎯 本篇重點
理解 Process 在記憶體中的結構:Code(程式碼)、Data(資料)、Stack(堆疊)、Heap(堆積)。
🏢 Process 記憶體結構總覽
Process 記憶體空間(4GB 範例)
┌─────────────────────────────────┐ ← 高位址 (0xFFFFFFFF)
│ Kernel Space │
│ (作業系統保留區域) │
├─────────────────────────────────┤ ← 0xC0000000
│ Stack ⬇️ │ 向下增長
│ (區域變數、函數呼叫) │
│ │
│ ↕️ 空閒空間 ↕️ │
│ │
│ Heap ⬆️ │ 向上增長
│ (動態分配的記憶體) │
├─────────────────────────────────┤
│ Data │
│ (全域變數、靜態變數) │
├─────────────────────────────────┤
│ Code │
│ (程式碼指令) │
└─────────────────────────────────┘ ← 低位址 (0x00000000)📦 四大區域詳解
1. Code Segment(程式碼段)
作用: 存放程式的執行指令(機器碼)
特性:
- ✅ 唯讀(Read-Only)
- ✅ 可共享(多個 Process 可共享同一份程式碼)
- ✅ 固定大小
# 這些程式碼會被編譯成機器碼,存放在 Code Segment
def add(a, b):
return a + b
def multiply(a, b):
return a * b
result = add(10, 20)比喻:
- Code Segment = 公司的 SOP 手冊
- 所有員工(Process)共用同一份 SOP
- SOP 不能修改(唯讀)
2. Data Segment(資料段)
作用: 存放全域變數、靜態變數
特性:
- ✅ 可讀寫
- ✅ 程式啟動時初始化
- ✅ 生命週期 = Process 生命週期
# 全域變數 → 存放在 Data Segment
counter = 0 # 初始化的全域變數
config = {"debug": True}
# 靜態變數(Python 沒有真正的靜態變數,但類變數類似)
class Config:
MAX_CONNECTIONS = 100 # 類變數 → Data Segment比喻:
- Data Segment = 公司的共用白板
- 所有函數都能看到和修改
- Process 結束時才清除
3. Stack(堆疊)
作用: 存放區域變數、函數呼叫資訊、返回位址
特性:
- ✅ LIFO(後進先出)
- ✅ 自動管理(函數結束自動釋放)
- ✅ 大小固定(通常 8MB)
- ✅ 向下增長
def function_a():
x = 10 # x 存放在 Stack
y = 20 # y 存放在 Stack
return function_b(x, y)
def function_b(a, b):
result = a + b # result 存放在 Stack
return result
function_a()Stack 的變化過程:
1. 呼叫 function_a()
Stack: [x=10, y=20]
2. 呼叫 function_b(10, 20)
Stack: [x=10, y=20] ← function_a 的 frame
[a=10, b=20, result=30] ← function_b 的 frame
3. function_b 返回
Stack: [x=10, y=20] ← function_b 的變數已清除
4. function_a 返回
Stack: [] ← 所有變數都清除了比喻:
- Stack = 餐廳的點餐單疊
- 最後點的最先做(LIFO)
- 做完就扔掉(自動清除)
4. Heap(堆積)
作用: 存放動態分配的記憶體
特性:
- ✅ 手動管理(需要明確分配和釋放)
- ✅ 大小靈活
- ✅ 向上增長
- ⚠️ 需要注意記憶體洩漏
# Python 中的 Heap 使用(自動垃圾回收)
class User:
def __init__(self, name):
self.name = name
# 創建物件 → 在 Heap 分配記憶體
user1 = User("Alice") # Heap: User 物件
user2 = User("Bob") # Heap: 另一個 User 物件
# Python 的垃圾回收會自動清理不再使用的物件
user1 = None # User("Alice") 會被垃圾回收C 語言範例(手動管理):
#include <stdlib.h>
int main() {
// 在 Heap 分配記憶體
int *ptr = (int *)malloc(sizeof(int) * 100);
// 使用記憶體
ptr[0] = 42;
// 必須手動釋放,否則記憶體洩漏!
free(ptr);
return 0;
}比喻:
- Heap = 倉庫
- 需要空間時自己申請
- 不用了要記得歸還
🔍 完整範例
Python 範例:各區域的變數
import sys
# Data Segment:全域變數
global_var = 100
global_list = [1, 2, 3]
class Calculator:
# Data Segment:類變數
version = "1.0"
def __init__(self, name):
# Heap:實例變數(物件存在 Heap)
self.name = name
def calculate(self, a, b):
# Stack:區域變數
result = a + b
temp = result * 2
return temp
def main():
# Stack:區域變數
x = 10
y = 20
# Heap:創建物件
calc = Calculator("MyCalc")
# Stack:函數呼叫
answer = calc.calculate(x, y)
print(f"答案: {answer}")
# Code Segment:函數定義
main()記憶體分布:
Stack:
├─ main() 的 frame
│ ├─ x = 10
│ ├─ y = 20
│ └─ calc (引用,指向 Heap 中的物件)
└─ calculate() 的 frame
├─ a = 10
├─ b = 20
├─ result = 30
└─ temp = 60
Heap:
├─ Calculator 物件 (name="MyCalc")
└─ global_list [1, 2, 3]
Data Segment:
├─ global_var = 100
├─ Calculator.version = "1.0"
└─ global_list (引用,指向 Heap)
Code Segment:
├─ Calculator.calculate() 的機器碼
└─ main() 的機器碼📊 四大區域對比
| 區域 | 存放內容 | 大小 | 生命週期 | 管理方式 |
|---|---|---|---|---|
| Code | 程式碼指令 | 固定 | Process 全程 | 作業系統 |
| Data | 全域變數、靜態變數 | 固定 | Process 全程 | 作業系統 |
| Stack | 區域變數、函數呼叫 | 固定(~8MB) | 函數呼叫期間 | 自動(LIFO) |
| Heap | 動態分配物件 | 靈活 | 明確釋放前 | 手動/GC |
⚠️ 常見問題
問題 1:Stack Overflow(堆疊溢位)
原因: 遞迴太深或區域變數太大
def recursive(n):
if n == 0:
return
x = [0] * 1000000 # 大型區域變數
recursive(n - 1)
# Stack Overflow!
recursive(10000)錯誤訊息:
RecursionError: maximum recursion depth exceeded解決方法:
- 減少遞迴深度
- 使用迭代代替遞迴
- 將大型資料放到 Heap
問題 2:Memory Leak(記憶體洩漏)
原因: Heap 的記憶體沒有釋放
class DataProcessor:
_cache = {} # 類變數,永遠不會被清除
def process(self, data):
# 不斷累積,記憶體洩漏!
self._cache[len(self._cache)] = data
# 每次呼叫都累積在 _cache 中
processor = DataProcessor()
for i in range(1000000):
processor.process(f"data_{i}")解決方法:
from collections import OrderedDict
class DataProcessor:
def __init__(self, max_cache=1000):
self._cache = OrderedDict()
self._max_cache = max_cache
def process(self, data):
self._cache[len(self._cache)] = data
# 限制 cache 大小
if len(self._cache) > self._max_cache:
self._cache.popitem(last=False)問題 3:Stack vs Heap 如何選擇?
| 情況 | 使用 Stack | 使用 Heap |
|---|---|---|
| 小型區域變數 | ✅ | ❌ |
| 大型資料結構 | ❌ | ✅ |
| 需要跨函數使用 | ❌ | ✅ |
| 生命週期 = 函數 | ✅ | ❌ |
| 需要動態大小 | ❌ | ✅ |
def example():
# Stack:小型、短生命週期
counter = 0
temp_sum = 100
# Heap:大型、長生命週期
large_data = [0] * 1000000
user_object = User("Alice")
return user_object # 返回 Heap 中的物件🎯 實用工具
查看 Process 記憶體使用
import os
import psutil
def print_memory_info():
process = psutil.Process(os.getpid())
mem_info = process.memory_info()
print(f"RSS (實際使用): {mem_info.rss / 1024 / 1024:.2f} MB")
print(f"VMS (虛擬記憶體): {mem_info.vms / 1024 / 1024:.2f} MB")
# 測試記憶體增長
print("啟動時:")
print_memory_info()
# 分配大量 Heap 記憶體
large_list = [0] * 10000000
print("\n分配記憶體後:")
print_memory_info()✅ 重點回顧
Process 記憶體四大區域:
Code Segment
- 程式碼指令
- 唯讀、可共享
Data Segment
- 全域變數、靜態變數
- Process 全程存在
Stack
- 區域變數、函數呼叫
- 自動管理、LIFO
Heap
- 動態分配記憶體
- 手動管理(或 GC)
關鍵理解:
- ✅ Stack 適合小型、短期資料
- ✅ Heap 適合大型、長期資料
- ✅ 注意 Stack Overflow 和 Memory Leak
- ✅ Python 的 GC 會自動管理 Heap
上一篇: 01-1. Process 是什麼 下一篇: 01-3. Process 的生命週期
最後更新:2025-01-04