어도비 데브넷에 Flash Player 9에서의 가비지 컬렉션 아티클이 올라와, 관심 있는 분께 도움이 될 듯 해서 올려봅니다.


Flash Player 9에서의 가비지 컬렉션 이해

Grant Skinner | CEO 겸 아키텍트 부문 책임자

원문보기

한동안 ActionScript 3.0을 사용해본 결과 필자는 이제 그 기능에 완전히 매료되었습니다. 자체의 순수한 실행 속도만으로도 많은 가능성을 제공합니다. E4X, 소켓, 바이트 배열, 새 디스플레이 목록 모델, RegEx 메서드, 형식화된 이벤트 및 오류 모델, 기타 훌륭한 여러 기능을 맘껏 사용하며 상당히 흥미로운 혼합 툴을 만끽할 수 있습니다.

강한 힘에는 그만큼의 책임이 따른다는 말은 ActionScript 3.0에 딱 어울리는 말입니다. 완전히 새로워진 이 컨트롤의 단점이 있다면 그것은 가비지 수집기가 언제 자동 정돈을 수행할지를 더 이상 예측할 수 없다는 것입니다. 즉, ActionScript 3.0을 사용하는 Flash 개발자는 가비지 수집기의 작동 방식과 효과적인 사용 방법에 대해 매우 잘 알고 있어야 한다는 뜻입니다. 이러한 지식이 없으면 단순한 게임이나 애플리케이션을 만들더라도 마치 여과기처럼 모두 새나가는 SWF가 만들어져 모든 시스템 리소스(CPU/RAM)를 독차지하고 시스템 오류, 심지어 컴퓨터를 강제로 다시 부팅해야 하는 상황이 발생할 수 있습니다.

ActionScript 3.0에 맞게 코드를 최적화하는 방법을 이해하려면 우선 Flash Player 9에서 가비지 수집기가 작동하는 방식을 이해해야 합니다. Flash에는 사용되지 않는 객체를 찾고 이를 제거하는 두 개의 프로세스가 있습니다. 본 기술문서에서는 이 두 가지 기법에 대해 살펴보고 이들이 코드와 어떤 관계가 있는지에 대해 설명합니다.

본 기술문서 끝부분에서는 여기서 설명한 개념을 실제로 보여주는 Flash Player 9의 가비지 수집기 시뮬레이션이 제공됩니다.

가비지 수집기

가비지 수집기는 애플리케이션에서 더 이상 사용하지 않지만 객체에서 사용하고 있는 메모리의 할당을 해제하는 역할을 담당하는, 배후에서 작동하는 프로세스입니다. 비활성 객체란 자신을 참조하는 기타 활성 객체가 없는 객체를 말합니다. 이를 이해하려면 기본 유형이 아닌 유형(Boolean, String, Number, uint, int 이외의 유형)으로 작업할 때 항상 객체에 객체 자체가 아니라 참조를 전달한다는 것을 깨닫는 것이 중요합니다. 변수를 삭제하면 객체 자체가 아니라 참조가 제거되는 것입니다.

다음 코드를 보면 이것을 쉽게 이해할 수 있습니다.

// create a new object, and put a reference to it in a:
var a:Object = {foo:"bar"}
// copy the reference to the object into b:
var b:Object = a;
// delete the reference to the object in a:
delete(a);
// check to see that the object is still referenced by b:
trace(b.foo); // traces "bar", so the object still exists.

위의 예제에서 코드를 업데이트하고 동시에 "b"를 삭제한다면 객체의 활성 참조가 없어져서 가비지 컬렉션용으로 비워지게 됩니다. ActionScript 3.0 가비지 수집기는 활성 참조가 없는 객체를 찾기 위해 두 가지 방법, 즉 참조 횟수(reference counting) 및 표시 회수(mark sweeping)를 사용합니다.

참조 횟수

참조 횟수는 활성 객체를 추적하는 가장 간단한 방법 중 하나이며 ActionScript 1.0부터 Flash에서 사용되었습니다. 객체에 대한 참조를 만들면 참조 횟수가 증가합니다. 참조를 삭제하면 참조 횟수가 감소합니다. 객체의 참조 횟수가 0이 되면 가비지 수집기에 의해 삭제됩니다.

예를 들면 다음과 같습니다.

var a:Object = {foo:"bar"}
// the object now has a reference count of 1 (a)
var b:Object = a;
// now it has a reference count of 2 (a & b)
delete(a);
// back to 1 (b)
delete(b);
// the reference count down is now 0
// the object can now be deallocated by the garbage collector

참조 횟수는 단순하면서 CPU 오버헤드를 크게 높이지 않기 때문에 대부분의 상황에 적합합니다. 그러나 순환 참조의 경우에는 참조 횟수가 가비지 컬렉션을 위한 최적의 방법이 아닙니다. 순환 참조란 객체가 서로를 교차 참조하는 상황을 말합니다(다른 객체를 통해 직접 또는 간접적으로). 애플리케이션이 해당 객체를 더 이상 사용하지 않더라도 참조 횟수가 0보다 큰 상태로 유지되므로 가비지 수집기가 해당 객체를 제거하지 않습니다. 다음 코드는 이러한 경우를 보여줍니다.

var a:Object = {}
// create a second object, and reference the first object:
var b:Object = {foo:a};
// make the first object reference the second as well:
a.foo = b;
// delete both active application references:
delete(a);
delete(b);

위 코드에서는 활성 애플리케이션 참조 두 개가 모두 삭제되었습니다. 이제 애플리케이션에서는 이 두 객체에 전혀 액세스하지 않지만 두 객체는 서로를 참조하므로 참조 횟수가 1입니다. 이러한 상황은 훨씬 더 복잡해질 수 있으며(ac를 참조하고, c는 b를 참조하고, 또 b는 a를 참조하는 등) 코드에서 다루기 힘들어질 수 있습니다. Flash Player 6과 7에는 XML 객체의 순환 참조와 관련된 문제가 있었습니다. 자식과 부모 모두를 참조하는 각 XML 노드가 있어 이들의 할당이 해제되지 않는 문제였습니다. 다행히도 Flash Player 8에는 표시(mark) 및 회수(sweep)라는 새로운 가비지 컬렉션 기법이 추가되었습니다.

표시-회수

ActionScript 3.0(및 Flash Player 8) 가비지 수집기에서 비활성 객체를 찾기 위해 사용되는 두 번째 전략은 표시 및 회수라는 방법입니다. Flash Player는 애플리케이션의 루트 객체에서 시작하여(ActionScript 3.0에서는 편리하게 "루트"라고 함) 그 내부의 모든 참조를 거치면서 발견되는 모든 객체를 표시합니다.

그런 다음 Flash Player는 표시된 각 객체를 반복합니다. Flash Player는 애플리케이션의 전체 객체 트리를 검토할 때까지 이 동작을 재귀적으로 계속 수행하며, 활성 참조를 통해 도달할 수 있는 모든 것을 표시합니다. 이 프로세스가 끝나면 Flash Player는 메모리에 있으면서 표시되지 않은 모든 객체는 더 이상 활성 참조가 없으므로 안전하게 할당 해제할 수 있다고 가정할 수 있습니다. 그림 1은 이것의 작동 방식을 보여줍니다. 녹색 참조는 표시하는 동안 Flash Player를 따라온 것이고, 녹색 객체는 표시된 것이고, 흰색 객체는 할당 해제될 것입니다.

사용자 삽입 이미지










그림 1. Flash Player에서 표시 및 회수 방법을 통해 활성 참조가 없는 객체 식별

표시 및 회수는 매우 정확합니다. 그러나 Flash Player가 전체 객체 구조를 검토해야 하므로 CPU 사용 관점에서 볼 때 이 프로세스에는 비용이 많이 듭니다. Flash Player 9에서는 표시 및 회수를 반복적으로 수행하고(동시에 모든 프레임이 아니라 여러 프레임씩 나누어 프로세스 진행) 이 프로세스를 가끔 실행함으로써 이 비용을 줄입니다.

가비지 수집기의 지연 및 불확정성

Flash Player 9에서는 가비지 수집기의 작동이 지연됩니다. 이것은 매우 중요한 개념으로서 반드시 이해해야 합니다. 모든 활성 참조가 삭제되더라도 객체가 즉시 제거되지 않고, 향후 불확정적인 시간에 제거됩니다(개발자 관점에서 볼 때). 가비지 수집기는 일련의 추론을 사용하고 무엇보다도 RAM 할당 및 메모리 스택의 크기를 관찰하여 실행 시기를 결정합니다. 개발자 입장에서는 비활성 객체가 언제 할당 해제될지 알 수 없다는 사실을 받아들여야 합니다. 또한 가비지 수집기가 비활성 객체의 할당을 해제할 때까지 비활성 객체가 계속해서 실행될 수 있다는 점을 인식해야 합니다. 따라서 할당이 해제되기 전에는 코드도 계속 실행되고(enterFrame 이벤트가 계속 발생), 사운드도 계속 재생되고, 로드도 계속 발생하며, 기타 이벤트도 계속 일어나게 됩니다.

Flash Player의 가비지 수집기가 객체의 할당을 해제하는 시기를 개발자가 전혀 제어할 수 없다는 사실을 기억하는 것이 매우 중요합니다. 개발자라면 게임과 애플리케이션을 완료할 때 그곳의 객체를 최대한 자신이 원하는 대로 제어하고 싶을 것입니다. 이러한 프로세스의 관리 전략은 필자의 동료가 작성한 Flash Player 9의 리소스 관리 전략 기술문서에 소개되어 있습니다.

다음 가비지 컬렉션 시뮬레이션에서 전체 메모리의 톱니 모양을 살펴보십시오(그림 2 또는 아래 링크 클릭). 수집기가 회수를 수행할 때 하강이 발생합니다. 차트를 자세히 보려면 클릭하십시오. 일시 중지하거나 다시 시작하려면 스페이스바를 누르고, 실행되는 동안 메모리 사용 추세를 변경하려면 위/아래 화살표를 누르십시오.

사용자 삽입 이미지

그림 2.
가비지 컬렉션 시뮬레이션

다음 시뮬레이션에서는(그림 3 또는 아래 링크 클릭) 객체(둥근 직사각형) 및 객체에 대한 참조를 드래그하십시오. 그런 다음 참조 횟수 또는 표시 및 회수를 실행하여 어떤 객체가 수집되는지 살펴보십시오. 객체에 있는 숫자는 해당 객체의 참조 횟수를 나타냅니다.

사용자 삽입 이미지






















그림 3.
가비지 컬렉션 시뮬레이션: 표시(mark) 및 회수(sweep)

다음 단계로

가비지 컬렉션에 대한 이해는 Flash 프로젝트가 사용자의 컴퓨터에 최소한의 영향을 주며 실행될 수 있도록 보장하는 최적화된 코드를 작성하기 위한 가장 중요한 단계입니다. 필자의 동료가 작성한 기술문서, Flash Player 9의 리소스 관리 전략을 읽어보고 Flash 개발자 센터Flash Player 개발자 센터를 방문하십시오.

Posted by 알 수 없는 사용자