development-logo
소프트웨어를 개발할 때 메모리 관리 방식은 프로그램의 안정성과 성능을 좌우하는 핵심 요소입니다. 특히 자바스크립트, Python, C# 같은 매니지드 언어(Managed Language)에서는 가비지 컬렉터(Garbage Collector)가 자동으로 메모리를 수집하고 해제하여 개발자가 직접 메모리 할당·해제에 신경 쓰지 않아도 되지만, 내부적으로 어떤 원리로 동작하는지를 이해하는 일은 여전히 중요합니다.
이번 스터디에서는 “그림으로 쉽게 배우는 자료구조와 알고리즘 (심화편)” 강의를 통해, 다양한 자료구조와 알고리즘의 개념을 학습해 왔습니다. 그중 하나의 미션으로, AVL 트리를 이용해 가비지 컬렉터의 메모리 검색 기능을 직접 구현해 보고, 그 과정을 정리하여 공유하고자 합니다.
이번 미션에서는 “매니지드 언어의 가비지 컬렉터에서 메모리 검색 기능을 구현”하는 것이 목표입니다.
요구사항은 다음과 같습니다.
전제 조건: 같은 크기의 빈 메모리 블록은 존재하지 않는다고 가정합니다.
빈 메모리 블록을 삽입/삭제/검색하는 연산이 모두 O(log N)에 이루어지는 AVL 트리를 사용합니다.
import { AVLTree } from "../../avlTree/avlTree.mjs";
class GarbageCollector {
constructor() {
this.avlTree = new AVLTree();
}
// 빈 메모리 블록 삽입
insertFreeMemory(size) {
this.avlTree.root = this.avlTree.insert(this.avlTree.root, size);
}
// 요청 크기 이상인 블록 검색
searchFreeMemory(requestSize) {
let current = this.avlTree.root;
let candidate = null;
while (current) {
const nodeSize = current.getData();
if (nodeSize === requestSize) {
// 정확히 같은 크기를 찾으면 즉시 반환
candidate = current;
break;
}
if (nodeSize > requestSize) {
// 후보로 저장한 뒤, 더 작은 후보를 찾기 위해 왼쪽 서브트리 탐색
candidate = current;
current = current.getLeftSubTree();
} else {
// 노드 크기가 요청 크기보다 작으면 오른쪽 서브트리로 탐색
current = current.getRightSubTree();
}
}
return candidate;
}
// 반환된 블록(크기) 삭제
releaseFreeMemory(size) {
this.avlTree.root = this.avlTree.remove(this.avlTree.root, size);
}
}
JavaScriptimport { AVLTree } from "../../avlTree/avlTree.mjs";
class GarbageCollector {
constructor() {
this.avlTree = new AVLTree();
}
// 빈 메모리 블록 삽입
insertFreeMemory(size) {
this.avlTree.root = this.avlTree.insert(this.avlTree.root, size);
}
// 요청 크기 이상인 블록 검색
searchFreeMemory(requestSize) {
let current = this.avlTree.root;
let candidate = null;
while (current) {
const nodeSize = current.getData();
if (nodeSize === requestSize) {
candidate = current;
break;
}
if (nodeSize > requestSize) {
candidate = current;
current = current.getLeftSubTree();
} else {
current = current.getRightSubTree();
}
}
return candidate;
}
// 반환된 블록(크기) 삭제
releaseFreeMemory(size) {
this.avlTree.root = this.avlTree.remove(this.avlTree.root, size);
}
}
// 실행
const gc = new GabageCollector();
console.log("========== 빈 메모리 영역 초기화 ==========");
gc.insertFreeMemory(64); // 빈 64바이트 삽입
gc.insertFreeMemory(48); // 빈 48바이트 삽입
gc.insertFreeMemory(87); // 빈 87바이트 삽입
gc.insertFreeMemory(13); // 빈 13바이트 삽입
gc.insertFreeMemory(102); // 빈 102바이트 삽입
gc.insertFreeMemory(34); // 빈 34바이트 삽입
gc.insertFreeMemory(61); // 빈 61바이트 삽입
gc.insertFreeMemory(40); // 빈 40바이트 삽입
gc.insertFreeMemory(6); // 빈 6바이트 삽입
// 64바이트 요청 → 64 찾기 → 삭제
let freeMemory1 = gc.searchFreeMemory(64); // 64바이트 메모리
console.log(freeMemory1);
if (freeMemory1) {
gc.releaseFreeMemory(freeMemory1.data);
}
// 42바이트 요청 → 42 이상 중 최소값(48)을 찾아 반환 → 삭제
let freeMemory2 = gc.searchFreeMemory(42); // 48바이트 메모리 획득
console.log(freeMemory2);
if (freeMemory2) {
gc.releaseFreeMemory(freeMemory2.data);
}
JavaScript아래는 위 코드를 Node.js에서 실행했을 때 출력된 콘솔 로그 입니다.
========== 빈 메모리 영역 초기화 ==========
BinaryTree {
data: 64,
leftSubTree: BinaryTree {
data: 48,
leftSubTree: BinaryTree {
data: 34,
leftSubTree: BinaryTree {
data: 13,
leftSubTree: [BinaryTree], // data: 6
rightSubTree: null,
height: 2
},
rightSubTree: BinaryTree {
data: 40,
leftSubTree: null,
rightSubTree: null,
height: 1
},
height: 3
},
rightSubTree: BinaryTree {
data: 61,
leftSubTree: null,
rightSubTree: null,
height: 1
},
height: 4
},
rightSubTree: BinaryTree {
data: 87,
leftSubTree: null,
rightSubTree: BinaryTree {
data: 102,
leftSubTree: null,
rightSubTree: null,
height: 1
},
height: 2
},
height: 5
}
BinaryTree {
data: 48,
leftSubTree: BinaryTree {
data: 34,
leftSubTree: BinaryTree {
data: 13,
leftSubTree: [BinaryTree], // data: 6
rightSubTree: null,
height: 2
},
rightSubTree: BinaryTree {
data: 40,
leftSubTree: null,
rightSubTree: null,
height: 1
},
height: 3
},
rightSubTree: null,
height: 4
}
PowerShell세부 노드 제거 및 회전 과정을 거쳐 AVL 균형이 정상적으로 유지됨을 확인할 수 있습니다. 따라서 요구사항대로 “정확히 같은 크기 노드 우선 반환 → 삭제”와 “크기 이상의 값 중 최소값 반환 → 삭제”가 올바르게 구현되었음을 검증했습니다.
이번 미션을 통해, “AVL 트리”가 단순히 학습용 자료구조가 아니라, 실제 가비지 컬렉터 같은 시스템 구성 요소에서 어떻게 활용될 수 있는지 이해할 수 있었습니다. 앞으로도 이론을 실제 코드로 구현하며 자료구조와 알고리즘의 응용력을 지속해서 키워 나가겠습니다.
들어가며 소프트웨어 개발자는 코드가 어떻게 실행되는지 정확히 이해해야 할 필요가 있습니다. 우리가 작성한 프로그램은 결국…
서론 현대 웹 애플리케이션 아키텍처에서 웹 서버(Web Server) 와 웹 애플리케이션 서버(WAS, Web Application Server)…
HTTP 헤더(Header)란? HTTP(Header)는 클라이언트와 서버 간에 교환되는 메타데이터로, 요청(Request)과 응답(Response)에 부가적인 정보를 실어 나르는 역할을…
Readable Code: 읽기 좋은 코드를 작성하는 사고법Practical Testing: 실용적인 테스트 가이드 강의와 함께한 인프런 워밍업 클럽…
Readable Code: 읽기 좋은 코드를 작성하는 사고법Practical Testing: 실용적인 테스트 가이드 강의와 함께한 인프런 워밍업 클럽…
테스트 시 의존성 주입(Dependency Injection)과 Mockito Spring 애플리케이션을 개발하다 보면, 테스트 코드에서 실제 빈(Bean)을 사용하지…