플래시 플레이어의 가비지 컬렉션(gabage collection)에 대한 이해
by 세계의끝 on 12.01, 2010, under OOP, 고수들은 가르쳐주지 않는 AS3.0 입문
이 포스트는 “액션스크립트로 하는 객체 재사용(재활용)과 오브젝트 풀(Object Pool) – 개요” 로부터 이어지는 내용 입니다.
A. 가비지 컬렉션 일반론 Gabage Collection Overview
프로그래밍을 하면서 사용한 객체는 메모리를 소비해야 하기 때문에 운영체제에서 빌려와서 사용하게 됩니다. 메모리를 빌려와 그 공간에 객체를 기록하는 행위를 할당(allocation) 이라고 합니다. 그리고 객체를 사용한 후 더이상 필요가 없어지게 되면 객체를 운영체제에 반납하는 절차를 거쳐야 합니다. 마치 도서관에서 책(객체)을 대출한 후 열람실(메모리)에서 열람하는 것과 같다고 볼 수 있습니다.
그런데 프로그래밍이라는 것이 컴퓨터가 하는것이 아니라 개발자라는 사람이 하는 것이라, 운영체제에서 빌려온 메모리를 반환하지 않고 빼먹는 개발자가 있기도 합니다. 프로그램이 짧은 시간 동안 실행되고 종료 되는 것이라면 큰 문제는 아니지만, 서버용 프로그램과 같이 프로그램이 실행 한 후 오랜 시간 동안 실행 상태를 유지해야 하는 경우에는 메모리에 더 이상 사용하지 않게된 메모리들이 점점 쌓이는 한편, 새로운 메모리는 계속 요구 되기 때문에 언젠가는 메모리가 꽉 자서 옴짝 달싹 할 수 없게 됩니다. 이런 것을 메모리 유출(memory leek) 현상이라고 하여 프로그램 에러의 가장 치명적인 원인이 됩니다.
그래서 현대의 고급 프로그래밍 언어에는 이런 메모리 반납 과정을 자동으로 처리해 주는 Gabage Collection (이하 GC) 이라는 것이 생겨났습니다. 프로그래머가 사용한 후 필요 없어진 객체들은 GC가 정기적으로 수거하고 메모리에서 소멸시켜버려 다시 메모리를 사용할 수 있는 상태로 만들어주는거죠. 위에서 한 도서관의 비유를 계속 이어나가자면 가비지 컬렉션은, 우리가 그 존재를 눈치채기는 쉽지 않지만, 더이상 아무도 읽지 않거나 너무 낡아 책으로서의 가치가 없어진 것들을 수거해서 처리하는 사서에 비할 수 있을것 같습니다.
그러나 GC는 메모리 반환을 하기 귀찮아 하는 게으른 프로그래머를 위한 도구라기 보다는, 프로그래머가 메모리 반환 같은 단순한 일에 신경쓰지 않고 보다 큰 개념의 로직이나 구조적인 프로그래밍에 집중할 수 있도록 도와주는 역할을 한다고 보는것이 타당합니다.
B. Mark, Sweep and Compact
GC가 동작하는 방식을 그림으로 표현해 보겠습니다.

Mark, Sweep & Compact
- Mark – 참조되고 있지 않은 객체를 표시(mark) 한 후,
- Sweep – 표시된 객체를 쓸어(sweep) 버리고,
- Compact – 중간중간 구멍이 뚫려 있는 메모리 공간을 남아있는 객체를 스스슥 이동해서 디스크 조각모음을 하듯 공간을 확보합니다.[01]
이 과정을 개발자가 신경쓰지 않아도 플래시 플레이어의 GC가 수행합니다. 상당히 편리하겠죠? 객체가 변수에 대입되거나, addEventListener() 메서드가 달려있거나 하는 등의 참조가 되어있다면 GC가 mark 를 하지 않습니다. mark 가 되지 않으면 sweep 되지 않으므로 그 객체는 GC에 의해 삭제당할 가능성이 없는 상태가 됩니다.
위의 내용을 Grant Skinner가 시뮬레이션 할 수 있게 만들어 놓은 [링크] 를 확인해 보세요.
그러나 지금까지의 설명은 객체지향 프로그래밍의 GC에 대한 일반론에 불과합니다. 액션스크립트에서는 이와같은 일반론에 더해지는 몇 가지 특수한 상황들이 존재합니다.
C. 액션스크립트 개발자의 입장에서 본 GC
액션스크립트 역시 Java 의 연장선상에 있는 현대의 고급 프로그래밍 언어에 속하기 때문에 GC를 이용한 메모리 회수 방식을 사용합니다. 그런데 플래시 개발자들이 알아야만 하는 몇 가지 사항이 더 있습니다.
C-1. GC를 강제로 실행시킬 수 없다.
GC는 운영체제가 플래시 플레이어에게 할당한 일정 분량의 메모리가 고갈되어갈 무렵에 실행되기 때문에 그 실행 시점을 정확히 알 수 없습니다. 또한, 버그로 동작되는 상황을 제외하고는 개발자가 GC의 동작을 시작시킬 수 없습니다. NetConnection 객체를 고의로 두번 호출하면 이 객체를 회수하기 위해 GC가 동작하게 되고 try…catch로 감싸놓으면 런타임 에러가 발생하지 않게 되는 버그가 있습니다만, 정상적인 처리 방법이 아닐뿐더러 Adobe 에 의해 언제 패치될지도 모르므로 사용하지 않는것이 좋습니다.
C-2. GC가 도는 시점이 문제.
대부분의 플래시 플레이어는 웹 브라우저에 플러그인 형태로 실행되기 때문에 플래시 답게 항상 가벼운 동작을 기대하지만, 실제로 GC가 돌기 시작하는 시점은 이미 무거워질대로 무거워져 물기를 흠뻑 머금은 스펀지와 같은 상황일때가 되고 나서인 경우가 대부분 입니다. 우리가 플래시에게 기대하는 것은 이런것이 아니겠죠.
C-3. GC의 수거대상이 되게 하기 위한 준비작업의 번거로움.
객체를 가비지 컬렉션 대상이 되게 하기 위해서는 변수가 가리키는 모든 참조 제거, 객체에 붙여놓은 이벤트리스너 모두를 제거한 후, 변수에 null 을 대입해야 합니다. 이렇게 글로 표현하는것은 쉽지만, 실제 코드상에서는 이 객체가 어디서 몰래 참조되고 있는지 알 수 없는 경우도 꽤 됩니다. 게다가 이런 절차는 네이티브 개발자 출신이 아닌 경우가 많은 플래시 개발자의 입장에서는 번거롭게 느껴지기 마련이고, 그냥 내버려 두는 경우가 대부분이죠.
C-4. GC가 돌아도 문제.
우여곡절끝에 GC가 돌아도 문제가 여전히 남아 있습니다. GC가 실행될때는 프로그램이 일시적으로 중단되는 현상을 발견하게 됩니다. GC 대상이 많은 경우라면 체감할 수 있을 정도죠. 게다가 플래시 플레이어는 기본적으로 싱글 쓰레드 이기 때문에 프로그램 실행을 모두 일시 중단하고 GC를 돌린 후 다시 프로그램을 실행 하게 되므로 멈칫거리는 현상이 더욱 잘 느껴집니다.
D. Gabage Collection vs. Object Pool
플래시로 만들어진 대부분의 일반적인 웹 애플리케이션들은 GC에게 객체의 수명관리를 맡겨도 무방합니다. 어차피 플래시 애플리케이션은 대부분 웹 브라우저에 플러그인 형태로 보여지게 되고, 플래시가 차지하고 있던 메모리는 브라우저가 종료되는 시점에 동시에 회수되는 브라우저 의존적인 형태라 실행되는 지속 시간이 그리 길지 않기 때문이죠.
그러나 게임과 같이 생성해야 하는 객체의 숫자가 많고, 지속시간도 대체로 긴 편이면서, 그 객체가 차지하는 메모리 크기가 큰 경우에는 GC 보다는 객체 풀(Object Pool) 방식으로 객체를 재활용 관리하는 것이 보다 좋은 선택이라 할 수 있습니다.
객체 풀 방식에 대해서는 “액션스크립트의 객체 재사용을 위한 오브젝트 풀(Object Pool)” 에서 이어집니다.
- Java 에서는 이 compact 과정이 존재하는데, 플래시 플레이어가 이 과정을 수행하는지는 확실히 알려져 있지 않습니다. [↑]
Blog under the Creative Commons Attribution-NoDerivs 3.0 License