Garbage collection

From 탱이의 잡동사니
Jump to: navigation, search

Overview

Garbage collection - Automatic Memory allocating and deallocating 내용 정리

Basic

C/C++ 을 주로 사용하는 사람들에게는 Java 나 C# 에서 제공하는 가비지 컬렉터가 마치 모든 것을 해결해 줄것만 같은 도구일 수도 있다. 하지만 결론적으로, 그런것은 없다. 간단하게, 가비지 컬렉터는 "완벽하게" 동작하지는 않는다.

아래는 이와 같은 생각을 정리한 글이다[1].

자바나 C# 같은 언어들은 가비지 컬렉터(garbage collector, GC)가 있어 C/C++에서 골치 아픈 메모리 누수 문제를 해결해준다는 인식이 초보자 혹은 나 같이 GC를 많이 써보지 않은 사람에게 널리 퍼져있다. 
이건 정말 위험한 생각이다. 
GC는 일부 메모리 문제를 해결하지 모든 메모리 누수 문제를 해결해 줄 수 없다.

C/C++에서 흔히 겪는 메모리 문제를 정리하면:

    1. 메모리 오염(corruption) 문제:
        1.1 버퍼 오버플로우
        1.2 double free: free한 포인터를 또 free
        1.3 dangling pointers: 해제된 메모리를 다시 참조
    2. 메모리 누수

이렇게 크게 나눌 수 있는데 1의 문제는 GC로 해결 가능하다. 
단순 배열에 대한 버퍼 오버플로우는 mananged 환경의 언어들의 배열 범위 검사로 해결된다. 
명시적으로 free/delete를 할 필요 없으니 double free 같은 문제도 없다. 
그런데 메모리 누수는 GC라 해도 완벽히 잡아낼 수 없다. 

메모리 누수를 다시 두 개로 나눠 생각하면:

    2.1 접근 불가능한 객체에 대한 누수(lost objects): 더 이상 해당 객체에 접근을 할 수 없지만 반환되지 않은 것들
    2.2 더 이상 사용하지 않는 객체에 대한 누수(useless objects): 도달은 가능한데 더 이상 사용하지 않는 것들

핵심은 두 번째 해당하는 녀석인데, 먼저 여기서 아주 간략히 GC의 작동 원리부터 이야기 하면, 사실 별 것 없다. 
근본 원리는 할당 받은 메모리에 접근할 수 있는 경로가 더 이상 존재하지 않는다면 이 메모리는 사용되지 않는 것으로 간주하고 GC가 자동적으로 반환하는 것이다. 
코드까지 쓰기는 귀찮고(…) 대표적인 예로 메모리를 할당 받은 변수가 지역 변수라서 해당 스코프를 벗어나면 더 이상 접근 불가능한 경우가 있다.

그러나 문제는 두 번째에 해당하는 문제, 즉 도달은 가능한데 더 이상 사용하지 않는 객체에 대한 메모리 누수이다. 
조금 극단적인 경우지만 이런 서버 프로그램의 예를 생각해보자.

    클라이언트가 접속할 때 마다 Client라는 자료 구조를 하나 할당하고 전역 리스트에 이 Client 객체를 넣는다.
    클라이언트가 접속을 끊으면 해당 Client 자료 구조를 리스트에서 삭제한다.

만약 이렇게만 작동한다면 GC는 똑똑히 작동한다. 
2번 과정에서 더 이상 해당 Client 자료 구조에 대한 접근이 사라지므로 GC는 이 객체를 해제할 수 있다. 
C/C++ 처럼 명시적으로 Client 객체를 free/delete 할 필요는 없다.

그러나 문제는 프로그램이 복잡해지면 2번 과정에 실수가 있을 수 있다. 
분명 더 이상 객체가 사용되지 않는데 프로그램 어딘가에 이 객체로 접근할 수 있는 길이 남아 있는 것이다. 
극단적으로 이야기 하면 프로그래머가 2번 과정을 실수로 빼먹는 경우다. 
아무리 이 Client 객체가 사용되지 않지만 전역 리스트에 자리를 차지하고 있으니 GC는 이것이 언젠가는 사용될 것이라고 보수적으로 판단해서 삭제를 할 수 없다.

어떤 메모리가 오랫동안 사용되지 않는다 하더라도 GC는 이 메모리 영역을 해제할 수 없다. 
극단적으로 어떤 객체가 백 만년 뒤에 쓰인다면? 이건 구현이 어려워서가 아니라 아예 불가능한 영역이 있다. 
미래를 예측할 수 없기 때문이다. 
GC 구현 기법 중 Generational GC 등이 있는데 이건 접근 불가능한 객체들을 효율적으로 찾기 위한 것이지, 이렇게 더 이상 사용되지 않는, 그런데 도달은 가능한 객체에 대한 누수를 해결하는 것이 아니다.

따라서 어떤 객체로 접근할 수 있는 방법이 있다면 GC는 아무것도 할 수 없고 실제 큰 자바 프로젝트(예를 들어 느려터진 이클립스)에서는 메모리 누수가 많이 일어난다. 
Managed 환경에서도 메모리 사용을 프로파일링 하는 도구는 많이 있고 메모리 누수 검출 도구도 많이 있다. 
.NET Memory Profiler, JRockit 같은 것이 일단 있고 C/C++ 쪽은 익히 아는 DevPartner나 GNU 환경에서는 Valgrind 같은 것들이 있다.

위키에도 이런 내용이 잘 있다. 
물리적 메모리 누수(lost objects)와 논리적 메모리 누수(useless objects)로 분류하고 GC는 후자에 대해서는 아무것도 할 수 없다.

결론: 가비지 컬렉터가 있어도 메모리 누수는 발생합니다.

Garbage collectors

C

C 에는 가비지 컬렉터가 없다. 하지만 라이브러리 형태로 만들어진 것들이 있다.

  • Boehmgc - Boehm garbage collector
http://hboehm.info/gc/

See also

References

  1. http://minjang.egloos.com/2372567