포스트

운영체제

메모리 관리란?

운영체제가 메모리를 효율적으로 할당하고 회수하는 과정

메모리 관리가 필요한 이유

  • 제한된 메모리 : 물리적 메모리는 한정적
  • 다중 프로세스 : 여러 프로세스가 동시에 실행
  • 효율성 : 메모리 공간을 최적화해야 함

메모리 할당 방식

연속 메모리 할당

고정 분할 (Fixed Partition)

  • 메모리를 고정된 크기로 미리 분할
  • 간단하지만 내부 단편화 발생

가변 분할 (Variable Partition)

  • 프로세스 크기에 맞게 메모리 분할
  • 외부 단편화 발생 가능
1
2
3
4
메모리 상태 예시:
[프로세스A][빈공간][프로세스B][빈공간][프로세스C]
         ↑          ↑
    내부 단편화   외부 단편화

불연속 메모리 할당

페이징 (Paging)

  • 물리 메모리를 고정 크기 블록(페이지)으로 분할
  • 논리 주소를 물리 주소로 변환
1
2
3
논리 주소: [페이지 번호][페이지 오프셋]
        ↓ 페이지 테이블 참조
물리 주소: [프레임 번호][페이지 오프셋]

세그멘테이션 (Segmentation)

  • 논리적 단위(세그먼트)로 메모리 분할
  • 코드, 데이터, 스택 등을 별도 세그먼트로 관리

가상 메모리

물리 메모리보다 큰 프로그램을 실행할 수 있게 하는 기술

주요 개념

페이지 폴트 (Page Fault)

  • 요청한 페이지가 물리 메모리에 없을 때 발생
  • 디스크에서 해당 페이지를 메모리로 로드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 페이지 폴트 처리 과정
def page_fault_handler(page_number):
    if page_number not in physical_memory:
        # 1. 디스크에서 페이지 로드
        page_data = load_from_disk(page_number)
        
        # 2. 물리 메모리에 공간이 없으면 교체
        if physical_memory.is_full():
            victim_page = select_victim_page()  # LRU, FIFO 등
            swap_out(victim_page)
        
        # 3. 새 페이지를 메모리에 로드
        physical_memory.load(page_data)
        
        # 4. 페이지 테이블 업데이트
        update_page_table(page_number)

페이지 교체 알고리즘

FIFO (First In First Out)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class FIFO:
    def __init__(self, capacity):
        self.capacity = capacity
        self.memory = []
        self.queue = []
    
    def access_page(self, page):
        if page not in self.memory:
            if len(self.memory) >= self.capacity:
                # 가장 먼저 들어온 페이지 제거
                oldest_page = self.queue.pop(0)
                self.memory.remove(oldest_page)
            
            self.memory.append(page)
            self.queue.append(page)
            return "Page Fault"
        return "Hit"

LRU (Least Recently Used)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class LRU:
    def __init__(self, capacity):
        self.capacity = capacity
        self.memory = {}
        self.access_order = []
    
    def access_page(self, page):
        if page in self.memory:
            # 최근 사용으로 갱신
            self.access_order.remove(page)
            self.access_order.append(page)
            return "Hit"
        
        if len(self.memory) >= self.capacity:
            # 가장 오래 사용되지 않은 페이지 제거
            lru_page = self.access_order.pop(0)
            del self.memory[lru_page]
        
        self.memory[page] = True
        self.access_order.append(page)
        return "Page Fault"

LFU (Least Frequently Used)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class LFU:
    def __init__(self, capacity):
        self.capacity = capacity
        self.memory = {}
        self.frequency = {}
    
    def access_page(self, page):
        if page in self.memory:
            self.frequency[page] += 1
            return "Hit"
        
        if len(self.memory) >= self.capacity:
            # 가장 적게 사용된 페이지 찾기
            min_freq = min(self.frequency.values())
            lfu_page = next(p for p in self.frequency 
                          if self.frequency[p] == min_freq)
            
            del self.memory[lfu_page]
            del self.frequency[lfu_page]
        
        self.memory[page] = True
        self.frequency[page] = 1
        return "Page Fault"

메모리 단편화

내부 단편화 (Internal Fragmentation)

1
2
3
할당된 메모리 블록:
[프로세스 데이터][사용되지 않는 공간]
                 ↑ 내부 단편화
  • 할당된 메모리 블록 내부에서 사용되지 않는 공간
  • 고정 크기 할당에서 주로 발생

외부 단편화 (External Fragmentation)

1
2
3
4
5
전체 메모리:
[프로세스A][빈공간][프로세스B][빈공간][프로세스C]
          ↑ 2KB     ↑ 3KB
          
새로운 4KB 프로세스 → 할당 불가 (총 5KB 여유 공간이 있지만)
  • 할당되지 않은 메모리가 작은 조각들로 나뉘어져 있는 상태
  • 가변 크기 할당에서 발생

압축 (Compaction)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def memory_compaction(memory_blocks):
    """메모리 압축을 통한 외부 단편화 해결"""
    allocated = []
    free_space = 0
    
    for block in memory_blocks:
        if block.is_allocated:
            allocated.append(block)
        else:
            free_space += block.size
    
    # 할당된 블록들을 앞으로 이동
    compacted_memory = allocated + [FreeBlock(free_space)]
    return compacted_memory

실제 운영체제에서의 메모리 관리

Linux 메모리 관리

1
2
3
4
5
6
7
8
# 메모리 사용량 확인
free -h

# 프로세스별 메모리 사용량
ps aux --sort=-%mem | head

# 가상 메모리 정보 확인
cat /proc/meminfo

JavaScript V8 엔진

1
2
3
4
5
6
7
8
9
10
// 힙 메모리 사용량 확인
const memUsage = process.memoryUsage();
console.log('Heap Used:', memUsage.heapUsed);
console.log('Heap Total:', memUsage.heapTotal);

// 가비지 컬렉션 강제 실행
if (global.gc) {
    global.gc();
    console.log('GC executed');
}

메모리 누수 방지

JavaScript 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 문제가 있는 코드
let users = [];
function addUser(userData) {
    users.push(userData); // 계속 누적됨
}

// 개선된 코드
let users = new Map();
function addUser(userId, userData) {
    users.set(userId, userData);
    
    // 일정 크기 초과 시 오래된 데이터 제거
    if (users.size > MAX_USERS) {
        const oldestKey = users.keys().next().value;
        users.delete(oldestKey);
    }
}

Python 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
# 문제가 있는 코드
cache = {}
def get_data(key):
    if key not in cache:
        cache[key] = expensive_operation(key)  # 무한정 증가
    return cache[key]

# 개선된 코드 - LRU Cache 사용
from functools import lru_cache

@lru_cache(maxsize=128)
def get_data(key):
    return expensive_operation(key)

성능 최적화 팁

지역성 (Locality) 활용

1
2
3
4
5
6
7
8
9
# 나쁜 예 - 공간 지역성 위반
for j in range(cols):
    for i in range(rows):
        matrix[i][j] = process_data(i, j)

# 좋은 예 - 공간 지역성 활용
for i in range(rows):
    for j in range(cols):
        matrix[i][j] = process_data(i, j)

메모리 풀 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class MemoryPool:
    def __init__(self, block_size, pool_size):
        self.block_size = block_size
        self.free_blocks = [bytearray(block_size) 
                           for _ in range(pool_size)]
        self.used_blocks = []
    
    def allocate(self):
        if self.free_blocks:
            block = self.free_blocks.pop()
            self.used_blocks.append(block)
            return block
        return None
    
    def deallocate(self, block):
        if block in self.used_blocks:
            self.used_blocks.remove(block)
            self.free_blocks.append(block)
이 글은 저작권자의 CC BY 4.0 라이센스를 따릅니다.