프로그래밍을 할 때 메모리 관리는 필수 불가결하다.
프로그래머가 직접 메모리를 할당과 해제를 관리한다면 실수로 인해 사용 중인 메모리가 어떠한 이유로 해제되어 프로그램이 고장 날 수도 있고, 메모리 누수가 일어날 수도 있다.
이러한 문제를 해결하기 위해 직접 할당과 해제를 하지 않고 메모리를 관리하는 여러 시스템이 만들어졌다.
이 글에선 메모리를 관리하는 시스템 3가지를 소개하려 한다.
1. 참조 카운팅(Reference Counting)
이 방법은 주로 C++에서 사용되는 방법으로, 객체를 참조하는 카운팅을 통해 객체의 할당 해제 시점을 결정하는 방식이다.
C++에서 '스마트 포인터'를 사용해 카운팅 한다. 객체를 참조하면 카운트가 1이 늘어나고, 참조를 하는 객체가 소멸된다면 카운트가 1이 줄어든다. 최종적으로 카운트가 0이 된다면 메모리가 해제된다.
장점
가장 단순 명쾌한 방식으로, 사용자가 임의로 해제를 하지 않아도 알아서 해제가 되는 간편성이 있다.
단점
- 순환 참조: A라는 객체에선 B를 참조하고, B라는 객체에선 A를 참조하고 있을 때, 서로의 참조 카운트가 1이 되므로 영원히 해제가 되지 않는 문제가 발생한다. 물론 약한 참조라는 해결법도 있다.
- 성능 오버헤드: C++에서는 '복사 생성자'와 '대입 연산자'를 통해 카운트를 올린다. 하지만 불필요하게 반복되는 복사 연산은 오버헤드가 발생할 수 있다.
2. GC(Garbage Collection)
이 방법은 Java와 C#에서 사용되는 방법으로, Java로 개발한 프로그램을 작동시키면 자바 가상 머신(JVM)에서 Garbage Collector가 알아서 불필요한 메모리를 해제하는 시스템이다.
GC가 있는 언어라면 메모리의 할당과 해제는 개발자가 직접 관리하지 않아도 된다.
장점
개발자가 직접 메모리를 관리하지 않기 때문에 메모리로 인해 문제가 일어날 가능성이 거의 없고, 그로 인해 개발의 효율성이 높아진다.
단점
- 성능 오버헤드: Java에서 GC가 작동할 때 다른 동작이 중지됨과, 프로그램 이외로 GC가 작동해 CPU를 점유함으로 인해 성능의 오버헤드가 발생한다.
- 메모리 오버헤드: GC의 메모리 관리 알고리즘으로 인해 프로그래머가 의도한 메모리보다 더 많은 메모리가 과다 할당되는 메모리 오버헤드가 발생한다.
3. 소유권(Ownership)
이 방법은 Rust가 사용하는 제3의 방식이다. 컴파일 타임에 '소유권 규칙'에 따라 문법을 체크하는 방식을 사용해 메모리가 유효한지 체크하는 방식이다. (C++로 치자면 모든 객체가 unique_ptr인 셈)
소유권 규칙
- 각각의 '값'은 '오너(owner)'라고 불리는 변수를 갖고 있다.
- 하나의 '값'에 대해서는 하나의 오너만 존재할 수 있다.
- '오너'가 스코프 밖에 나갔을 때 '값'은 버려진다.
장점
컴파일 타임에 체크하므로, 런타임에 체크하는 GC방식에 비해 성능이 좋다.
단점
'소유권 규칙'으로 인해 문법 검사가 깐깐하게 진행된다.
'잡다지식' 카테고리의 다른 글
[Git] repo에서 다른 repo바로가기 만들기 (submodule) (0) | 2023.12.05 |
---|