서비스
교육 철학

오브젝트 풀링 (Object Pooling)

Abstract

📌
이 페이지는 오브젝트 풀의 안쪽을 다룹니다. HideEntity 로 숨긴 적이 왜 파괴되지 않는지, 언제 진짜로 파괴되는지, Capacity 와 Expire Time 이 정확히 무엇을 정하는지가 여기서 결정됩니다. 직접 풀을 만들어 쓰는 방법은 Unity 쪽 Object Pooling (오브젝트 풀링) 페이지에 예제로 정리했습니다.
엔티티 한 마리가 풀을 거치는 과정
ToyboxNightmare 에서 Zombunny 한 마리가 스폰되고 죽을 때 풀에서 일어나는 일입니다.
sequenceDiagram
    participant G as 게임 코드
    participant E as EntityManager
    participant P as ObjectPool (Enemy 그룹)
    G->>E: ShowEntity("Zombunny")
    E->>P: Spawn("Zombunny")
    alt 풀에 남은 인스턴스가 있다
        P-->>E: 기존 인스턴스 반환 (재사용)
    else 없다
        E->>E: 프리팹 로드 → Instantiate
        E->>P: Register(인스턴스, spawned: true)
    end
    Note over G,P: ... 전투 ...
    G->>E: HideEntity(enemy)
    E->>P: Unspawn(인스턴스)
    Note over P: 파괴하지 않고 보관.<br>마지막 사용 시각만 기록
Mermaid
복사
핵심은 Spawn(name) 의 name 이 Addressables 주소라는 점입니다. 같은 그룹 안에서도 Zombunny 인스턴스는 Zombunny 요청에만 재사용되고 Hellephant 요청에는 쓰이지 않습니다.
인스턴스는 언제 진짜로 파괴되나
풀이 보관 중인(사용 중이 아닌) 인스턴스는 다음 두 경우에 Release 됩니다.
조건
시점
예제의 값
용량 초과 — 풀의 전체 개수가 Capacity 를 넘는다
Unspawn 직후 즉시
Enemy 64, Projectile 64, Effect 32
만료 — Expire Time 동안 다시 쓰이지 않았다
Auto Release Interval 마다 도는 정기 검사
둘 다 60초
해제 순서는 "만료된 것 먼저 → 그다음 우선순위 낮은 것 → 같으면 오래 안 쓴 것"입니다. 사용 중인 인스턴스는 절대 해제되지 않으므로, Capacity 를 넘겨 스폰해도 스폰 자체는 성공합니다. Capacity 는 스폰 상한이 아니라 "쉬고 있는 것을 몇 개까지 남겨 둘지"에 가깝습니다.
튜닝 감각
•
Capacity 가 너무 작으면 웨이브마다 Instantiate / Destroy 가 반복되어 풀링 효과가 사라진다. 동시 최대 등장 수보다 약간 크게 잡는다
•
Expire Time 이 너무 짧으면 웨이브 사이 쉬는 동안 다 파괴되고 다음 웨이브에서 다시 만든다. 웨이브 간격보다 길게 잡는다
•
Expire Time 이 너무 길면 보스전에만 나오는 이펙트가 끝까지 메모리에 남는다. 그런 것은 그룹을 따로 만들어 짧게 준다
•
장면 전환처럼 "지금 당장 비우고 싶다"면 ObjectPoolComponent.ReleaseAllUnused() 를 부른다
Single Spawn / Multi Spawn 이 실제로 쓰이는 곳
풀
모드
이유
엔티티 인스턴스, UI 폼 인스턴스
Single
GameObject 하나를 두 곳에서 동시에 쓸 수 없다
(원작) 로드된 에셋, 번들
Multi
프리팹 원본 하나를 여러 인스턴스가 공유한다. Spawn 횟수가 곧 참조 카운트다
⚠️
이 에디션에서 다른 점 · 본문을 읽을 때 주의할 점
•
본문 "리소스 관리 시스템과의 통합" 그림의 AssetObject / ResourceObject 풀은 원작의 AssetBundle 로더가 쓰던 것입니다. 이 에디션은 Addressables 가 에셋 수명을 관리하므로 그 두 풀은 만들어지지 않습니다. 실행 중 Debugger 창에서 보이는 풀은 엔티티 그룹·UI 그룹의 인스턴스 풀뿐입니다.
•
본문은 FSM 이 오브젝트 풀을 쓴다고 적었는데, 실제로는 레퍼런스 풀을 씁니다. 본문도 고쳐 두었습니다.
•
본문의 필드 이름 m_Objects, m_ObjectMap 은 원작 표기입니다. 이 에디션의 소스에서는 mObjects, mObjectMap 처럼 밑줄 없는 m 접두사로 바뀌어 있습니다(다른 페이지의 필드 이름도 마찬가지입니다).
•
풀에 넣을 객체가 구현하는 ObjectBase 의 훅(OnSpawn, OnUnspawn, Release)은 게임 코드에서 protected override 로 씁니다.

오브젝트 풀링 (Object Pooling)

관련 소스 파일:
•
GameFramework/Fsm/Fsm.cs
•
GameFramework/Fsm/FsmManager.cs
•
GameFramework/ObjectPool/IObjectPool.cs
•
GameFramework/ObjectPool/IObjectPoolManager.cs
•
GameFramework/ObjectPool/ObjectPoolBase.cs
•
GameFramework/ObjectPool/ObjectPoolManager.ObjectPool.cs
•
GameFramework/ObjectPool/ObjectPoolManager.cs
•
GameFramework/Resource/ResourceManager.ResourceLoader.AssetObject.cs
•
GameFramework/Resource/ResourceManager.ResourceLoader.LoadResourceAgent.cs
•
GameFramework/Resource/ResourceManager.ResourceLoader.ResourceObject.cs

개요 (Overview)

오브젝트 풀링은 메모리 관리 기법으로, 객체를 반복적으로 생성하고 파괴하는 대신 재사용하여 성능을 향상시키는 기술입니다. GameFramework는 자주 사용되는 객체들의 수명 주기를 효율적으로 관리하는 강력한 오브젝트 풀링 시스템을 제공하여 메모리 단편화와 GC(가비지 컬렉션) 부하를 줄입니다.
GameFramework의 오브젝트 풀링 시스템으로 할 수 있는 일:
•
특정 객체 타입에 대한 풀 생성
•
필요할 때 풀에서 객체 가져오기(Spawn)
•
필요 없을 때 객체 반환(Unspawn)
•
정책에 따라 자동으로 사용하지 않는 객체 해제
•
객체 사용 내역 추적 및 해제 우선순위 관리
게임 개발에서 오브젝트 풀링은 투사체, 파티클, UI 요소, 게임 엔티티처럼 자주 생성/파괴되는 객체 관리에 특히 유용합니다.

시스템 아키텍처

오브젝트 풀링 시스템은 여러 핵심 컴포넌트로 구성되어 있으며, 함께 작동해 효율적인 객체 재사용을 제공합니다.
ObjectPoolManager는 여러 ObjectPool 인스턴스를 관리하며, 각 풀은 특정 타입의 객체를 담당합니다.

오브젝트 풀 매니저 (Object Pool Manager)

ObjectPoolManager는 오브젝트 풀을 생성 및 관리하는 중앙 접근 지점입니다.
•
각 풀은 TypeNamePair(객체 타입 + 풀 이름) 으로 식별됩니다.

주요 기능

•
단일 스폰(SingleSpawn) / 다중 스폰(MultiSpawn) 오브젝트 풀 생성
•
메모리 해제를 위한 풀 우선순위 관리
•
매 프레임 풀 업데이트
•
게임 종료 시 풀 종료

풀 생성 방식

•
CreateSingleSpawnObjectPool: 하나의 객체가 Unspawn되기 전까지 다시 Spawn될 수 없음
•
CreateMultiSpawnObjectPool: 하나의 객체가 동시에 여러 번 Spawn될 수 있음
설정 가능한 파라미터:
•
풀 이름
•
용량 (Capacity)
•
자동 해제 주기 (Auto-release interval)
•
만료 시간 (ExpireTime)
•
풀 우선순위

오브젝트 풀 구현 (Object Pool Implementation)

각 ObjectPool은 ObjectBase를 상속한 특정 타입의 객체를 관리합니다.
내부 구조:
•
m_Objects: 이름 기반으로 객체를 저장하는 Multi-dictionary
•
m_ObjectMap: 객체 → Object 래퍼 매핑 (빠른 조회용)

객체 생명주기

1.
등록 (Registration): Register(obj, spawned) 호출
2.
스폰 (Spawning): Spawn() 또는 Spawn(name) 호출
3.
언스폰 (Unspawning): Unspawn(obj) 호출
4.
해제 (Release): 풀 설정에 따라 사용하지 않는 객체 해제

풀 설정 파라미터

파라미터
설명
Name
풀 식별자
AllowMultiSpawn
객체를 여러 번 동시에 Spawn할 수 있는지 여부
AutoReleaseInterval
자동 해제 검사 주기 (초 단위)
Capacity
풀의 최대 객체 수
ExpireTime
사용하지 않는 객체가 해제 대상으로 되는 시간(초 단위)
Priority
풀 우선순위 (메모리 부족 시 낮은 우선순위부터 해제됨)

오브젝트 해제 메커니즘

자동 해제 (Auto-Release)

•
주기적으로 사용하지 않는 객체를 찾아 해제
•
대상 선정 기준:
◦
사용 중 아님
◦
잠금(lock) 상태 아님
◦
CustomCanReleaseFlag == true

수동 해제 (Manual Release)

•
Release(): 용량 초과 시 해제
•
Release(int count): 지정 개수만큼 해제
•
ReleaseAllUnused(): 사용하지 않는 객체 모두 해제

해제 필터링

•
먼저 만료된 객체 해제
•
그 후 우선순위(낮은 순) + 마지막 사용 시간(오래된 순) 기준으로 해제

객체 잠금과 우선순위

객체 잠금 (Locking)

objectPool.SetLocked(obj, true); // 해제 불가 objectPool.SetLocked(obj, false); // 해제 허용
C#
복사

객체 우선순위 (Prioritization)

objectPool.SetPriority(obj, 100); // 높은 우선순위 (늦게 해제) objectPool.SetPriority(obj, 0); // 낮은 우선순위 (먼저 해제)
C#
복사

다른 시스템과의 통합

•
리소스 관리 시스템:
•
FSM (Finite State Machine):
◦
FSM 은 오브젝트 풀이 아니라 레퍼런스 풀(ReferencePool.Acquire<Fsm<T>>())을 쓴다. 관련 소스 목록에 Fsm 파일이 있지만 오브젝트 풀과는 무관하다
•
엔티티, UI, 사운드 시스템:
◦
풀링을 통해 생성/파괴 비용 절감

베스트 프랙티스 (Best Practices)

•
풀링 적합 대상: 자주 생성/삭제되는 객체
•
풀 설정:
◦
용량(capacity) → 예상 최대치 기반
◦
만료 시간(expire time) → 사용 패턴 기반
◦
자동 해제 주기(auto-release interval) → 메모리 상황 고려
•
의존성 관리:
◦
종속 객체도 함께 등록 및 관리
◦
참조 카운트 조정
•
객체 정리:
◦
Unspawn 시 상태 초기화
◦
참조 해제 (메모리 누수 방지)
◦
CustomCanReleaseFlag 정확히 설정
•
풀 종류 선택:
◦
SingleSpawn → 동시 사용 불가 객체
◦
MultiSpawn → 공유 가능한 객체

성능 고려사항

•
풀 크기 조정:
◦
너무 작으면 객체 생성/삭제 잦음
◦
너무 크면 메모리 낭비
•
자동 해제 주기 튜닝:
◦
짧게 설정 → 메모리 절약, CPU 부담 ↑
◦
길게 설정 → 메모리 사용 ↑, CPU 부담 ↓
•
우선순위로 중요한 객체 보호
•
초기화 단계에서 자주 쓰는 객체 미리 등록 (Prewarm)
•
GetAllObjectInfos()로 풀 상태 모니터링 및 조정