Garbage collection

From 탱이의 잡동사니
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

Overview

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

Basic

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

아래는 이와 같은 생각을 정리한 글이다<ref>http://minjang.egloos.com/2372567</ref>.

자바나 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

<references />