using System; using System.Collections.Concurrent; using UnityEngine; namespace UVC.Data { /// /// DataObject 인스턴스를 재사용하기 위한 객체 풀 클래스입니다. /// 메모리 할당과 가비지 컬렉션을 최소화하기 위해 DataObject 인스턴스를 관리합니다. /// /// /// 객체 풀링은 자주 생성 및 삭제되는 객체의 성능을 향상시키는 패턴입니다. /// 사용이 끝난 객체를 삭제하는 대신 풀에 반환하고, 새 객체가 필요할 때 풀에서 꺼내 재사용합니다. /// /// /// /// // 객체 풀에서 DataObject 가져오기 /// DataObject obj = DataObjectPool.Get(); /// /// // 객체 사용 /// obj["name"] = "홍길동"; /// obj["age"] = 30; /// /// // 작업 완료 후 풀에 반환 /// DataObjectPool.Return(obj); /// /// // 풀 통계 확인 /// ULog.Debug(DataObjectPool.GetStats()); /// /// public static class DataObjectPool { /// /// DataObject 인스턴스를 저장하는 스레드 안전 큐입니다. /// private static ConcurrentQueue dataObjectPool = new ConcurrentQueue(); /// /// 풀의 최대 크기입니다. 이 크기를 초과하는 객체는 풀에 저장되지 않습니다. /// private static int maxPoolSize = 1000; // --- 통계용 필드 --- private static int _inUseCount = 0; private static int _peakUsage = 0; private static int _poolMisses = 0; private static readonly object _statsLock = new object(); /// /// 풀의 최대 크기를 설정합니다. /// /// 설정할 최대 크기 public static void SetMaxPoolSize(int size) { maxPoolSize = size > 0 ? size : 1000; } /// /// 풀에서 DataObject 인스턴스를 가져옵니다. /// 풀이 비어있으면 새 인스턴스를 생성하여 반환합니다. /// /// 재사용 가능한 DataObject 인스턴스 public static DataObject Get() { bool fromPool = dataObjectPool.TryDequeue(out var obj); lock (_statsLock) { _inUseCount++; if (_inUseCount > _peakUsage) { _peakUsage = _inUseCount; } // 풀이 비어있어서 새 객체를 만들어야 하는 경우 if (!fromPool) { _poolMisses++; // 현재 사용량이 최대 풀 크기에 도달했는지 확인 if (_inUseCount > maxPoolSize) { int oldSize = maxPoolSize; maxPoolSize += 1000; Debug.Log($"DataObjectPool size automatically increased from {oldSize} to {maxPoolSize}. Peak usage: {_peakUsage}"); } } } if (_peakUsage % 100 == 0) Debug.Log($"DataObjectPool stats: {GetStats()}"); if (fromPool) { obj.IsInPool = false; obj.Reset(); } return fromPool ? obj : new DataObject(); } /// /// 사용이 완료된 DataObject를 풀에 반환합니다. /// 객체는 반환 전에 초기화되어 모든 속성이 제거됩니다. /// /// 풀에 반환할 DataObject 인스턴스 public static void Return(DataObject obj) { if (obj == null || obj.IsInPool) return; lock (_statsLock) { _inUseCount--; } if (dataObjectPool.Count < maxPoolSize) { obj.Reset(); // 재사용 전 완벽한 초기화 obj.IsInPool = true; dataObjectPool.Enqueue(obj); } // 풀이 가득 차면 객체는 풀에 추가되지 않고 GC 대상이 됩니다. } /// /// 풀의 현재 성능 통계를 문자열로 반환합니다. /// /// 풀 통계 (최대 사용량, 현재 사용량, 풀 비어있을 때 생성 횟수, 현재 풀 크기) public static string GetStats() { return $"Peak Usage: {_peakUsage}, In Use: {_inUseCount}, Misses: {_poolMisses}, Pooled: {dataObjectPool.Count}, Max Size: {maxPoolSize}"; } /// /// 풀 통계를 초기화합니다. /// public static void ResetStats() { lock (_statsLock) { _peakUsage = 0; _poolMisses = 0; } } } }