GC란?
-
GC(Garbage Collection)은 메모리 관리 기법 중의 하나이다.
-
프로그램이 동적으로 할당했던 메모리 영역 중에서 필요 없게 된 영역을 해제하는 기능이다.
GC 장단점
장점
-
GC를 지원하는 환경에서는
프로그래머가 동적으로 할당한 메모리 영역의 전체를 완벽하게 관리할 필요가 없어진다.
-
GC는 다음과 같은 버그를 줄이거나 완전히 막을 수 있다.
- 유효하지 않은 포인터 접근
- 이미 해제된 메모리에 접근하는 버그를 가리킨다.
- 만약 이 포인터가 해제되고 새로운 값이 할당되었다면, 잘못된 값을 읽어오게 된다.
- 이중 해제
- 이미 해제된 메모리를 다시 해제하는 버그를 가리킨다.
- 일부 메모리 할당 알고리즘에서는 해제된 메모리를 다시 해제하려고 시도하는 오류를 일으킬 수 있다.
- 메모리 누수
- 더는 필요하지 않은 메모리가 해제되지 않고 남아있는 버그를 가리킨다.
- 메모리 누수가 반복되면 메모리 고갈로 프로그램이 중단될 수 있다.
- 접근 가능한 메모리가 증가하여 메모리가 고갈되는 문제는 GC으로도 막을 수 없다.
- 유효하지 않은 포인터 접근
단점
-
어떤 메모리를 해제할지 결정하는 데 비용이 든다.
-
객체가 필요 없어지는 시점을 프로그래머가 미리 알고 있는 경우에도
GC 알고리즘이 메모리 해제 시점을 추적해야 하므로 이 작업은 오버헤드가 발생한다.
-
GC가 일어나는 타이밍이나 점유 시간을 예측하기 어렵다.
즉 할당된 메모리가 해제되는 시점을 알 수 없다.
그러므로 프로그램이 예측 불가능하게 일시적으로 정지할 수 있다.
이런 특성은 특히 실시간 시스템에는 적합하지 않다.
JVM 메모리 구조
-
아래 사진은 자바(JVM)의 메모리 구조이다.
크게 Heap / Perm / Thread로 나눌 수 있다.
-
Heap은 다시 Young(New) 영역과 Old 영역으로
Young 영역은 다시 Eden / Survior1 / Survior2 로 나눌 수 있다.
-
Young 영역은 객체가 처음 생성 시 저장되는 영역을 나타낸다.
정확하게 말하면 new 연산자를 통해 생성되는 객체들은 EdenSpace에 저장된다.
-
Old 영역은 Young 영역에 존재하던 객체들이 오래되어 저장되는 영역이다.
-
Perm 영역은 Class 혹은 Method 코드가 저장되는 영역이다.
하지만 Java 8 이상부터는 Perm 영역이 사라졌다.
관련해서는 Java 8에서 JVM의 변화 : PermGen이 사라지고 Metaspace가 등장하다. 글을 참고하자.
GC의 종류
-
Java의 GC는 크게 Minor GC와 Full GC로 나눌 수 있다.
-
그 중 Minor GC는 Young 영역에 발생하는 GC를 나타낸다.
-
GC 종류에 대한 자세한 내용은 가비지 컬렉션(Garbage Collection) 2편에서 알아본다.
Minor GC
- Young 영역에서 돌아가는 GC라고 생각하면 된다.
-
최초 Minor GC가 발생하기 전에
할당 되어있던 모든 객체는 Eden 영역에만 존재한다.
-
이때 Minor GC가 발생하면
Eden 영역 중 활성 객체가 Survivor1영역으로 이동한다.
이 후 비활성 객체만 남은 Eden 영역은 Clear 된다.
-
다음 Minor GC가 발생하면
최초 Minor GC 이후 생성된 객체들이 존재하는 Eden 영역의 객체들과
최초 Minor GC 이후 생존한 객체들이 저장된 Survivor1의 영역 객체들이
Survivor2로 이동하고
Eden 영역과 Survivor1영역이 Clear 된다.
-
이후 위 작업이 Survivor1과 Survivor2를 번갈아가면서 계속 수행되고
계속해서 활성 객체로 생존하는 객체들은 시간이 지남에 따라 Old 영역으로 이동한다.
Full GC
-
Old 영역에서 돌아가는 GC라고 생각하면 된다.
-
Full GC는 Old 영역이 Full이 되면 수행된다.
-
Full GC는 Old 영역을 스캔하여 객체들의 참조 상태를 표시하고
참조되지 않는 객체를 해제(Mark&Compact)하는 작업을 시행한다.
-
이러한 Full GC는 속도가 느리고 성능에 아주 큰 영향을 끼친다.
그 이유는 Full GC가 수행되는 동안 자바 애플리케이션은 멈춤 현상이 발생하기 때문이다.
포인터 추적 방식
-
대부분의 GC 기법은 포인터 추적 방식을 사용한다.
-
포인터 추적 방식은
한 개 이상의 변수가 접근 가능한 메모리는 앞으로 사용할 수 있는 메모리로 간주하고
그 밖의 메모리를 해제하는 방식을 가리킨다.
접근 가능한 객체
- 접근 가능한 객체는 어떤 변수가 직접 가리키는 메모리 또는 간접적으로 가리키는 메모리를 의미한다.
여러가지 포인터 추적 기법
-
포인터 추적 기법에는 여러가지 방법이 존재한다.
-
어떤 언어들은 다음 기법들 가운데 여러가지를 함께 사용하기도 한다.
표시하고 쓸기 (Mark and Sweep)
-
표시하고 쓸기 기법은 포인터 추적 기법 가운데 가장 단순한 기법이다.
-
먼저 각 메모리 할당 영역에 표시를 위해 1 비트의 메모리를 남겨 둔다.
표시 단계에서 모든 변수가 가리키는 영역을 사용 중으로 표시하고
그 영역에서 가리키는 또 다른 영역 또한 사용 중으로 표시한다.
-
이처럼 모든 메모리 영역을 표시하고 나면
표시되지 않은 영역을 접근 불가능한 메모리 영역이 된다.
-
접근 불가능한 메모리 영역들은 쓸기 단계에서 모두 해제한다.
-
이 기법의 단점은
표시 단계에서 메모리 내용이 변경되지 않아야 하므로 전체 시스템의 실행이 정지된다는 것이다.
-
또한 전체 메모리 영역을 검사해야 하므로
메모리 페이징을 사용하는 운영체제에서 프로그램의 성능이 저하될 수 있다.
삼색 표시 기법
-
표시하고 쓸기 기법 의 단점을 보완하기 위해 많은 언어는 삼색 표시 기법을 사용한다.
-
삼색 표시 기법은 기본적으로 표시하고 쓸기 와 같은 기법이지만
표시 단계에서 2가지가 아닌 3가지(흰색, 회색, 검은색) 정보 중 하나로 메모리를 표시한다.
-
이 기법은 다음과 같은 순서로 이루어진다.
-
각각의 객체를 흰색, 회색, 검은색으로 분류한다.
흰색은 더는 접근 불가능한 객체를 가리킨다.
회색은 접근 가능한 객체이지만 이 객체에서 가리키는 객체들은 아직 검사되지 않았음을 의미한다.
검은색은 이 영역에서 가리키는 객체들이 흰색 객체를 가리키지 않음을 의미한다.
-
GC가 동작 시
변수가 가리키는 객체들이 회색으로 표시되며
그 외의 모든 객체는 흰색으로 표시된다.
-
회색으로 표시된 객체 가운데 하나를 선택하여
검은색으로 표시하고 이 객체가 가리키는 모든 객체를 회색으로 표시한다.
-
회색 객체가 하나도 남지 않을 때까지 위 과정을 반복한다.
-
남은 흰색 객체는 접근 불가능한 객체이므로 모두 해제한다.
-
이 알고리즘은 표시하고 쓸기 알고리즘 과 달리
프로그램이 실행 중에도 병행하여 수행할 수 있다.
-
또한, 메모리가 고갈되었을 때
GC을 실행하는 것이 아니라 주기적으로 수집하는 것도 가능하다.
객체 이동 기법
-
객체 이동 기법은 사용 중인 객체를 다른 영역으로 복사하는 기법을 가리킨다.
-
객체를 복사하는 것은 언뜻 비효율적으로 여겨질 수도 있으나 다음과 같은 실용적인 장점을 가지고 있다.
-
해제된 후 재사용 가능한 영역과 사용 중인 영역을 표시하기 위해 추가적인 작업을 할 필요가 없다.
뇌피셜 :: 새로운 곳에서 공간은 무조건 사용할 수 있기 때문이지 않을까?
-
따라서 해제된 영역을 포인터로 관리하는 방식에 비해 할당과 해제가 빠르게 이루어진다.
-
-
할당된 메모리들이 단편화되는 것을 막을 수 있다.
-
연결 리스트와 같은 연결형 자료구조에서
서로 연결된 객체들이 메모리상에서 가까운 위치에 할당될 확률이 높아진다.
이는 캐시와 관련하여 성능 향상에 도움이 된다.
-
반면, 메모리 이동 기법은 주기적으로 포인터의 내용이 바뀌므로 포인터 연산을 사용할 수 없게 된다는 단점이 있다.
세대 단위 GC
-
많은 연구자들은 프로그램에서 새롭게 할당된 영역일수록 금방 해제될 확률이 높다는 관찰을 보고하였다.
-
세대 단위 GC 기법은 이런 특성을 이용하여
각각의 객체를 할당된 시간에 따라 세대별로 구분하여
각 세대별로 서로 다른 메모리 영역에 객체를 할당한다.
-
만약 한 세대의 메모리 영역이 꽉 차면
이 메모리 영역에서 살아남은 객체를 더 오래된 메모리 영역으로 옮긴다.
-
새로 할당된 영역에서는 대부분의 객체가 빠르게 해제되고
오래된 영역에서는 객체들이 변하지 않을 확률이 높으므로
이 기법은 메모리의 일부 영역만을 주기적으로 수집하게 되는 장점이 있다.
-
자바, 닷넷 프레임워크 등 현대적 언어들은 대부분 이 기법을 사용한다.
메모리 관리
-
메모리 관리는 컴퓨터 메모리를 관리하는 행위이다.
-
가장 단순한 형태의 메모리 관리 방법은
프로그램의 요청이 있을 때 메모리의 일부를 해당 프로그램에 할당하고
더 이상 필요하지 않을 때 나중에 다시 사용할 수 있도록 해제하는 것이다.
-
이는 하나 이상의 프로세스가 언제든 실행되는 고급 컴퓨터 시스템에 필수적이다.
-
가상 메모리 시스템은 프로세스가 사용하는 메모리 주소를 실제 물리 주소와 구분한다.
이로써 프로세스를 구분하고
디스크 스왑 처리를 사용하여 효과적으로 사용할 수 있는 램의 양을 늘릴 수 있게 된다.
-
GC는 프로그램을 위한 컴퓨터 메모리 리소스를 자동으로 할당하고 할당을 해제한다.
-
프로그래밍 언어 수준에서 일반적으로 추가된 것이며 수동 메모리 관리와 대조된다.
메모리 누수
-
메모리 누수(memory leak) 현상은 컴퓨터 프로그램이 필요하지 않은 메모리를 계속 점유하는 현상이다.
-
할당된 메모리를 사용한 다음 반환하지 않는 것이 누적되면 메모리가 낭비된다.