์นดํ…Œ๊ณ ๋ฆฌ ์—†์Œ

Unity DOTS ECS ๋Œ€๊ทœ๋ชจ ์—”ํ‹ฐํ‹ฐ ์—ฐ์‚ฐ ์‹œ NativeArray ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜์™€ Job System ๋™๊ธฐํ™” ๋ณ‘๋ชฉ ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…

๊ฒŒ์ž„๊ต์ˆ˜ 2026. 8. 28. 01:01
๋ฐ˜์‘ํ˜•

๐ŸŒ English Abstract

This article provides an in-depth troubleshooting guide for handling NativeArray memory leaks and C# Job System synchronization bottlenecks in Unity DOTS (Data-Oriented Technology Stack) ECS environments. It explores allocator lifecycles, memory safety checks, Structural Changes, and EntityCommandBuffer optimization strategies when dealing with large-scale entity calculations. Developers will gain actionable insights on resolving main-thread stalls and optimizing memory performance for enterprise-level Unity projects.

์œ ๋‹ˆํ‹ฐ์˜ DOTS(Data-Oriented Technology Stack)์™€ ECS(Entity Component System)๋Š” ์ˆ˜๋งŒ ๊ฐœ ์ด์ƒ์˜ ๋Œ€๊ทœ๋ชจ ์—”ํ‹ฐํ‹ฐ(Entity)๋ฅผ ๊ณ ์„ฑ๋Šฅ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๊ฐ•๋ ฅํ•œ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค. ์บ์‹œ ์ง€์—ญ์„ฑ(Cache Locality)์„ ๊ทน๋Œ€ํ™”ํ•˜๊ณ  Burst ์ปดํŒŒ์ผ๋Ÿฌ(Burst Compiler)์™€ C# Job System์„ ํ™œ์šฉํ•˜์—ฌ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜(Garbage Collection) ์—†๋Š” ๊ทนํ•œ์˜ ์—ฐ์‚ฐ ์†๋„๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์‹ค์ œ ์‹ค๋ฌด ํ”„๋กœ์ ํŠธ์—์„œ ๋Œ€๊ทœ๋ชจ ์—ฐ์‚ฐ์„ ๊ตฌํ˜„ํ•˜๋‹ค ๋ณด๋ฉด ์˜ˆ๊ธฐ์น˜ ๋ชปํ•œ ๋‘ ๊ฐ€์ง€ ๊ฑฐ๋Œ€ํ•œ ์žฅ๋ฒฝ์— ์ง๋ฉดํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋ฐ”๋กœ NativeArray ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜(Memory Leak)์™€ Job System ๋™๊ธฐํ™” ๋ณ‘๋ชฉ(Synchronization Bottleneck / Sync Point) ํ˜„์ƒ์ž…๋‹ˆ๋‹ค. ๊ด€๋ฆฌ๋˜๋Š” ๋ฉ”๋ชจ๋ฆฌ(Managed Memory)์— ์ต์ˆ™ํ•œ C# ๊ฐœ๋ฐœ์ž๊ฐ€ ๋น„๊ด€๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ(Unmanaged Memory)๋ฅผ ์ œ์–ดํ•  ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ์ ๋“ค์„ ํŒŒ์•…ํ•˜๊ณ , ์ด๋ฅผ ์‹œ์Šคํ…œ์ ์œผ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๊นŠ์ด ์žˆ๋Š” ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ๊ฐ€์ด๋“œ๋ฅผ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค.



1. NativeArray ๋ฉ”๋ชจ๋ฆฌ ๋ชจ๋ธ ๋ฐ ๋ˆ„์ˆ˜(Memory Leak) ์›์ธ ๋ถ„์„

DOTS ECS ํ™˜๊ฒฝ์—์„œ๋Š” C#์˜ GC ๊ด€๋ฆฌ๋ฅผ ๋ฐ›๋Š” ๋ฐฐ์—ด ๋Œ€์‹ , C++ ํž™(Heap) ์˜์—ญ์— ์ง์ ‘ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹นํ•˜๋Š” ๋„ค์ดํ‹ฐ๋ธŒ ์ปจํ…Œ์ด๋„ˆ(Native Container)์ธ NativeArray<T>, NativeList<T>, NativeHashMap<K,V> ๋“ฑ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด ์ปจํ…Œ์ด๋„ˆ๋“ค์€ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ์˜ ์ถ”์ ์„ ๋ฐ›์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ์ƒ๋ช…์ฃผ๊ธฐ(Lifecycle)๋ฅผ ๊ด€๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

1.1 ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น์ž(Allocator)์˜ ์ดํ•ด์™€ ์˜ค์šฉ ์ผ€์ด์Šค

NativeArray๋ฅผ ํ• ๋‹นํ•  ๋•Œ๋Š” ์—ฐ์‚ฐ์˜ ๋ชฉ์ ๊ณผ ์ˆ˜๋ช…์— ๋งž๋Š” ํ• ๋‹น์ž(Allocator) ์œ ํ˜•์„ ์„ ํƒํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์„ ํƒ์ด ์ž˜๋ชป๋  ๊ฒฝ์šฐ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋‚˜ ํฌ๋ž˜์‹œ(Crash)๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

  • Allocator.Temp: ๊ฐ€์žฅ ๋น ๋ฅธ ํ• ๋‹น ์†๋„๋ฅผ ์ž๋ž‘ํ•˜๋ฉฐ, ๋‹จ์ผ ํ”„๋ ˆ์ž„(Single Frame) ๋‚ด์—์„œ ํ• ๋‹น ๋ฐ ํ•ด์ œ(Dispose)๊ฐ€ ์™„๋ฃŒ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. 1ํ”„๋ ˆ์ž„์„ ์ดˆ๊ณผํ•˜์—ฌ ์œ ์ง€๋˜๊ฑฐ๋‚˜ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ Job ๋‚ด๋ถ€๋กœ ์ง์ ‘ ์ „๋‹ฌ๋˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
  • Allocator.TempJob: ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ Job์— ์ „๋‹ฌํ•  ๋•Œ ์‚ฌ์šฉ๋˜๋ฉฐ, ์ตœ๋Œ€ 4ํ”„๋ ˆ์ž„ ์ด๋‚ด์— ํ•ด์ œ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ•ด์ œํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ์œ ๋‹ˆํ‹ฐ ์—๋””ํ„ฐ์—์„œ ๊ฒฝ๊ณ  ๋ฉ”์„ธ์ง€(Leak Warning)๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.
  • Allocator.Persistent: ์ˆ˜๋ช…์ด ๊ธธ๊ฑฐ๋‚˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด์— ๊ฑธ์ณ ์œ ์ง€๋˜์–ด์•ผ ํ•˜๋Š” ๋ฐ์ดํ„ฐ์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. C++์˜ malloc๊ณผ ๋™์ผํ•˜๋ฏ€๋กœ ๋ช…์‹œ์ ์œผ๋กœ Dispose()๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š์œผ๋ฉด ์•ฑ์ด ์ข…๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

1.2 ์‹ค๋ฌด์—์„œ ์ž์ฃผ ๋ฐœ์ƒํ•˜๋Š” ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ํŒจํ„ด (Edge Cases)

๋Œ€๊ทœ๋ชจ ECS ์‹œ์Šคํ…œ์—์„œ ๊ฐ€์žฅ ๋งŽ์ด ๋ฐœ์ƒํ•˜๋Š” ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ํŒจํ„ด์€ "Job์ด ์™„๋ฃŒ๋˜๊ธฐ ์ „์— ์Šค๋ ˆ๋“œ๊ฐ€ ์˜ˆ์™ธ(Exception)๋ฅผ ์ผ์œผ์ผœ Dispose()์— ๋„๋‹ฌํ•˜์ง€ ๋ชปํ•˜๋Š” ์ผ€์ด์Šค"์™€ "๋™์ ์œผ๋กœ ์ƒ์„ฑ๋œ NativeArray๋ฅผ ์ˆ˜์‹ ํ•˜๋Š” JobHandle ์ฒด์ธ์˜ ์œ ์‹ค"์ž…๋‹ˆ๋‹ค.

์•„๋ž˜๋Š” ์ž˜๋ชป ์ž‘์„ฑ๋œ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น ์ฝ”๋“œ์˜ ์ „ํ˜•์ ์ธ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค:

// โŒ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ž˜๋ชป๋œ ์ฝ”๋“œ ์˜ˆ์‹œ
public partial struct MovementSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        // ๋งค ํ”„๋ ˆ์ž„ Persistent ํ• ๋‹น
        NativeArray<float3> positions = new NativeArray<float3>(100000, Allocator.Persistent);

        var job = new ProcessPositionJob { Positions = positions };
        JobHandle jobHandle = job.Schedule(state.Dependency);

        // state.Dependency๋ฅผ ์—…๋ฐ์ดํŠธํ•˜์ง€ ์•Š๊ณ  Dispose๋„ ์ƒ๋žตํ•จ!
        // ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋งค ํ”„๋ ˆ์ž„ 100,000 * 12 Bytes ์”ฉ ๋ˆ„์ˆ˜๋จ
    }
}

1.3 ์˜ฌ๋ฐ”๋ฅธ Dispose ์ฒ˜๋ฆฌ ๊ธฐ๋ฒ•: [DeallocateOnJobCompletion]๊ณผ DisposeJob

๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ž‘์—… ์™„๋ฃŒ ํ›„ ์ž๋™์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ•ด์ œํ•˜๋Š” ์†์„ฑ(Attribute)์„ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ์•ˆ์ „ํ•œ ์˜์กด์„ฑ ๊ด€๋ฆฌ ๊ธฐ๋ฒ•์„ ๋„์ž…ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

// โญ• ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ์™„์ „ํžˆ ๋ฐฉ์ง€ํ•˜๋Š” ์˜ฌ๋ฐ”๋ฅธ ์ฝ”๋“œ
[BurstCompile]
public struct ProcessPositionJob : IJobParallelFor
{
    // Job ์™„๋ฃŒ ์‹œ ์ž๋™์œผ๋กœ NativeArray๋ฅผ ํ•ด์ œํ•จ
    [DeallocateOnJobCompletion]
    [ReadOnly] public NativeArray<float3> Positions;

    public void Execute(int index)
    {
        // ์—ฐ์‚ฐ ๋กœ์ง ์ˆ˜ํ–‰
    }
}

public partial struct MovementSystem : ISystem
{
    [BurstCompile]
    public void OnUpdate(ref SystemState state)
    {
        NativeArray<float3> positions = new NativeArray<float3>(100000, Allocator.TempJob);

        var job = new ProcessPositionJob { Positions = positions };
        
        // JobHandle ์ฒด์ธ ๋ช…์‹œ์  ์—ฐ๊ฒฐ
        state.Dependency = job.Schedule(positions.Length, 64, state.Dependency);
        
        // ๋˜๋Š” ๋ช…์‹œ์ ์œผ๋กœ DisposeJob ์‚ฌ์šฉ ๊ฐ€๋Šฅ:
        // state.Dependency = positions.Dispose(state.Dependency);
    }
}


2. Job System ๋™๊ธฐํ™” ๋ณ‘๋ชฉ(Sync Point)๊ณผ ์„ฑ๋Šฅ ์ €ํ•˜ ์š”์†Œ

DOTS ECS์˜ ๋‘ ๋ฒˆ์งธ ์ฃผ์š” ๋‚œ๊ด€์€ **๋™๊ธฐํ™” ๋ณ‘๋ชฉ(Synchronization Bottleneck)**์ž…๋‹ˆ๋‹ค. ์•„๋ฌด๋ฆฌ ๋งŽ์€ CPU ์ฝ”์–ด๋ฅผ ํ™œ์šฉํ•˜๋”๋ผ๋„, ๋ฉ”์ธ ์Šค๋ ˆ๋“œ(Main Thread)์™€ ์›Œ์ปค ์Šค๋ ˆ๋“œ(Worker Thread)๊ฐ€ ์„œ๋กœ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” **์Šคํ†จ(Stall)** ํ˜„์ƒ์ด ๋ฐœ์ƒํ•˜๋ฉด ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ์˜ ์ด์ ์ด ์ƒ์‡„๋ฉ๋‹ˆ๋‹ค.

2.1 ์กฐ๊ธฐ JobHandle.Complete() ํ˜ธ์ถœ๋กœ ์ธํ•œ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ ๋Œ€๊ธฐ

๊ฐ€์žฅ ํ”ํ•œ ๋ณ‘๋ชฉ์˜ ์›์ธ์€ JobHandle.Complete()๋ฅผ ํ•„์š” ์ด์ƒ์œผ๋กœ ๋นจ๋ฆฌ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ๋ฉ”์„œ๋“œ๋Š” ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ์—์„œ ์ž‘๋™ ์ค‘์ธ ์—ฐ์‚ฐ์ด ๋๋‚  ๋•Œ๊นŒ์ง€ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋ฅผ ๊ฐ•์ œ๋กœ ๋ธ”๋กœํ‚น(Blocking)ํ•ฉ๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ ์ „๋žต: Complete() ํ˜ธ์ถœ์„ ์ตœ๋Œ€ํ•œ ๋Šฆ์ถ”๊ฑฐ๋‚˜, ๋‹ค์Œ ํ”„๋ ˆ์ž„๊นŒ์ง€ ์—ฐ๋™๋˜๋„๋ก ํ”„๋ ˆ์ž„ ๊ฐ„ ๋”œ๋ ˆ์ด๋ฅผ ํ—ˆ์šฉํ•˜๋Š” ์˜์กด์„ฑ ๊ตฌ์กฐ(Dependency Graph)๋กœ ์„ค๊ณ„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

2.2 ๊ตฌ์กฐ์  ๋ณ€๊ฒฝ(Structural Change)์ด ์•ผ๊ธฐํ•˜๋Š” ๊ธ€๋กœ๋ฒŒ ๋™๊ธฐํ™” ์ง€์ 

ECS ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ๊ตฌ์กฐ์  ๋ณ€๊ฒฝ(Structural Change)์ด๋ž€ ์ฒญํฌ(Chunk)์˜ ๋ฉ”๋ชจ๋ฆฌ ๋ ˆ์ด์•„์›ƒ์„ ๋ณ€ํ˜•ํ•˜๋Š” ๋ชจ๋“  ์ž‘์—…์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค:

  • ์—”ํ‹ฐํ‹ฐ ์ƒ์„ฑ(CreateEntity) ๋˜๋Š” ํŒŒ๊ดด(DestroyEntity)
  • ์ปดํฌ๋„ŒํŠธ ์ถ”๊ฐ€(AddComponent) ๋˜๋Š” ์ œ๊ฑฐ(RemoveComponent)
  • Shared Component ๊ฐ’์˜ ๋ณ€๊ฒฝ

๊ตฌ์กฐ์  ๋ณ€๊ฒฝ์ด ์ผ์–ด๋‚˜๋Š” ์ˆœ๊ฐ„, ์œ ๋‹ˆํ‹ฐ๋Š” ๋‚ด๋ถ€ ์ฒญํฌ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์žฌ๋ฐฐ์น˜(Reallocation)ํ•ด์•ผ ํ•˜๋ฏ€๋กœ ์‹คํ–‰ ์ค‘์ธ ๋ชจ๋“  Job์ด ๋๋‚  ๋•Œ๊นŒ์ง€ ๋ชจ๋“  ์Šค๋ ˆ๋“œ๋ฅผ ์ •์ง€์‹œํ‚ต๋‹ˆ๋‹ค. ์ด๋ฅผ **๋™๊ธฐํ™” ์ง€์ (Sync Point)**์ด๋ผ ๋ถ€๋ฅด๋ฉฐ, ๋ Œ๋”๋ง ํ”„๋ ˆ์ž„์ด ์‹ฌ๊ฐํ•˜๊ฒŒ ํŠ€๋Š” ์›์ธ์ด ๋ฉ๋‹ˆ๋‹ค.

2.3 EntityCommandBuffer(ECB)๋ฅผ ํ™œ์šฉํ•œ ๊ตฌ์กฐ์  ๋ณ€๊ฒฝ ์ง€์—ฐ ์ฒ˜๋ฆฌ

๋™๊ธฐํ™” ์ง€์  ํญํƒ„์„ ํ”ผํ•˜๋Š” ํ‘œ์ค€ ํ•ด๋ฒ•์€ EntityCommandBuffer(ECB)๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. Job ๋‚ด๋ถ€์—์„œ๋Š” ๊ตฌ์กฐ์  ๋ณ€๊ฒฝ ๋ช…๋ น์„ ๋ฒ„ํผ์— ๊ธฐ๋ก(Record)๋งŒ ํ•ด๋‘๊ณ , ํ”„๋ ˆ์ž„์˜ ํŠน์ • ์‹œ์ (์˜ˆ: EndSimulationEntityCommandBufferSystem)์— ์ผ๊ด„(Batch) ์ฒ˜๋ฆฌํ•˜๋Š” ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.

// โญ• ParallelWriter์™€ ECB๋ฅผ ์ด์šฉํ•œ ๋ณ‘๋ชฉ ์—†๋Š” ๊ตฌ์กฐ์  ๋ณ€๊ฒฝ ์˜ˆ์‹œ
[BurstCompile]
public struct DestroyExpiredEntitiesJob : IJobEntity
{
    public EntityCommandBuffer.ParallelWriter Sticker;

    // EntityAndIndexInQuery๋ฅผ ์ด์šฉํ•˜์—ฌ ์•ˆ์ „ํ•œ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ๋ณ‘๋ ฌ ๊ธฐ๋ก
    public void Execute(Entity entity, [EntityIndexInQuery] int sortIndex, in Lifetime lifetime)
    {
        if (lifetime.Value <= 0)
        {
            // ์ง์ ‘ ํŒŒ๊ดดํ•˜์ง€ ์•Š๊ณ  ๋ฒ„ํผ์— ์˜ˆ์•ฝ
            Sticker.DestroyEntity(sortIndex, entity);
        }
    }
}

public partial struct LifetimeSystem : ISystem
{
    [BurstCompile]
    public void OnUpdate(ref SystemState state)
    {
        var ecbSingleton = SystemAPI.GetSingleton<EndSimulationEntityCommandBufferSystem.Singleton>();
        var ecb = ecbSingleton.CreateCommandBuffer(state.WorldUnmanaged);

        var job = new DestroyExpiredEntitiesJob
        {
            Sticker = ecb.AsParallelWriter()
        };

        state.Dependency = job.ScheduleParallel(state.Dependency);
    }
}

3. ์‹ค๋ฌด ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ์ฒดํฌ๋ฆฌ์ŠคํŠธ ๋ฐ ํ”„๋กœํŒŒ์ผ๋ง ๊ธฐ๋ฒ•

๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜์™€ ๋ณ‘๋ชฉ ํ˜„์ƒ์„ ์ •ํ™•ํ•˜๊ฒŒ ์ง„๋‹จํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์œ ๋‹ˆํ‹ฐ์—์„œ ์ œ๊ณตํ•˜๋Š” ์ •๊ตํ•œ ํ”„๋กœํŒŒ์ผ๋ง ๋„๊ตฌ๋ฅผ ์—ฐ๋™ํ•˜์—ฌ ๊ด€์ฐฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  1. Native Memory Leak Detector ํ™œ์„ฑํ™”: ํ”„๋กœ์ ํŠธ ๋นŒ๋“œ ์˜ต์…˜ ๋˜๋Š” ์—๋””ํ„ฐ ์„ค์ •์—์„œ Jobs -> Burst -> Safety Checks๋ฅผ ํ™œ์„ฑํ™”ํ•˜์—ฌ leak ๊ฒฝ๊ณ  ๋ฉ”์‹œ์ง€์˜ ์Šคํƒ ํŠธ๋ ˆ์ด์Šค(Stack Trace)๋ฅผ ํ™•๋ณดํ•ฉ๋‹ˆ๋‹ค.
  2. Unity Profiler์˜ Timeline View ํ™œ์šฉ: Profiler ํ•˜๋‹จ์˜ Timeline ๋ทฐ๋ฅผ ํ†ตํ•ด ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์— WaitForJob ๋˜๋Š” DOTS Sync Point๋กœ ์ธํ•œ ๊ฑฐ๋Œ€ํ•œ ๊ฐญ(Gap)์ด ์กด์žฌํ•˜๋Š”์ง€ ๋ชจ๋‹ˆํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค.
  3. Memory Profiler ํŒจํ‚ค์ง€ ์‚ฌ์šฉ: ์Šค๋ƒ…์ƒท(Snapshot)์„ ๋‘ ๋ฒˆ ์ดฌ์˜ํ•˜์—ฌ ๋น„๊ต(Diff)ํ•จ์œผ๋กœ์จ ์–ด๋–ค NativeArray๋‚˜ NativeUnsafePtr๊ฐ€ ์ •๋ฆฌ๋˜์ง€ ์•Š๊ณ  ๋ˆ„์ ๋˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.


4. ๊ฒฐ๋ก  (Conclusion)

Unity DOTS ECS ํ™˜๊ฒฝ์—์„œ ์ˆ˜๋งŒ ๊ฐœ ์ด์ƒ์˜ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ์—ฐ์‚ฐํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” C# ์—ฐ์‚ฐ ํšจ์œจํ™”๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ํ•˜๋“œ์›จ์–ด ๋ ˆ์ด์–ด์— ๊ทผ์ ‘ํ•œ ๋น„๊ด€๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ ๋ฐ ์Šค๋ ˆ๋“œ ๊ด€๋ฆฌ๊ฐ€ ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค.

NativeArray์˜ ์ˆ˜๋ช…์„ Allocator ๋ฐ [DeallocateOnJobCompletion]์„ ์ด์šฉํ•ด ๋ช…ํ™•ํžˆ ์ œ์–ดํ•˜๊ณ , ๊ตฌ์กฐ์  ๋ณ€๊ฒฝ์„ EntityCommandBufferSystem์œผ๋กœ ์ง€์—ฐ์‹œ์ผœ ๋™๊ธฐํ™” ๋ณ‘๋ชฉ(Sync Point)์„ ์ง€์›Œ๋‚˜๊ฐ€๋Š” ๊ฒƒ์ด ๋Œ€๊ทœ๋ชจ ์•„ํ‚คํ…์ฒ˜ ๊ตฌ์ถ•์˜ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค. ์‹œ์Šคํ…œ ์„ค๊ณ„ ๋‹จ๊ณ„๋ถ€ํ„ฐ ์„ ์ œ์ ์ธ ํ”„๋กœํŒŒ์ผ๋ง๊ณผ ์™„๋ฒฝํ•œ ์˜์กด์„ฑ ๊ด€๋ฆฌ ํŒจํ„ด์„ ๋„์ž…ํ•˜์—ฌ ์ตœ๊ณ  ์ˆ˜์ค€์˜ ํ”„๋ ˆ์ž„ ์œ ์ง€๋ ฅ์„ ํ™•๋ณดํ•˜์‹œ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜•