How to Debug and Fix Resource Leaks in a TEngine Project: A Complete Guide
Resource leaks in TEngine occur when AssetHandle objects or their AssetObject wrappers are not disposed or returned to the object pool, and can be fixed by implementing proper disposal guards in ResourceModule.Shutdown, clearing pools in ObjectPoolModule, and using try-finally blocks for async asset loading.
The TEngine framework (alex-rachel/tengine) is a Unity-based game architecture that integrates the YooAsset pipeline for resource management. Understanding how to debug and fix resource leaks in a TEngine project requires familiarity with its modular design, where the ResourceModule manages native asset handles through pooled AssetObject wrappers. When these wrappers are not properly released during scene transitions or async cancellations, native Unity resources accumulate in memory indefinitely.
Understanding TEngine's Resource Architecture
The framework follows a modular architecture where subsystems implement IModule or IUpdateModule contracts. The resource management layer consists of three critical components that interact during asset loading and disposal.
Key Components
ResourceModule (UnityProject/Assets/TEngine/Runtime/Module/ResourceModule/ResourceModule.cs) serves as the core manager for asset loading and package handling. It maintains internal dictionaries such as _assetInfoMap that track loaded assets throughout the application lifecycle.
AssetObject (UnityProject/Assets/TEngine/Runtime/Module/ResourceModule/ResourceModule.AssetObject.cs) wraps a YooAsset.AssetHandle and inherits from ObjectBase, enabling it to participate in the framework's pooling system. This wrapper is responsible for the final disposal of native Unity resources.
ObjectPoolModule (UnityProject/Assets/TEngine/Runtime/Module/ObjectPoolModule/ObjectPoolModule.cs) manages MemoryPool<T> instances that recycle AssetObject instances to avoid GC pressure. The module maintains internal dictionaries of pools that persist until explicitly cleared.
Common Causes of Resource Leaks in TEngine
Resource leaks typically manifest through three specific failure paths in the TEngine source code.
Forgotten Release Calls
The AssetObject.Release method only disposes the underlying AssetHandle when isShutdown is false. If a scene unloads but the resource module remains active, handles stay alive indefinitely because the release logic is bypassed during active runtime.
Object Pool Cleanup Failures
ObjectPoolModule does not automatically clear its internal dictionaries during Shutdown. Pooled AssetObject instances containing stale AssetHandle references remain in memory, preventing garbage collection of the wrapped native resources.
Async Operation Aborts
When download operations are cancelled before completion, AssetHandle references may persist inside unfinished UnityWebRequestOperation instances. The DisposeRequest method in YooAsset is only called during normal completion paths, leaving handles dangling when operations abort early.
Debugging Resource Leaks Step-by-Step
Follow this diagnostic procedure to identify leak sources in your TEngine implementation.
1. Enable Lifecycle Logging
Add diagnostic logging inside AssetObject.Release and ObjectPoolModule to track object creation and disposal timing:
// Inside AssetObject.Release (temporary debug addition)
protected internal override void Release(bool isShutdown)
{
Log.Info($"Releasing AssetHandle for '{_resourceModule?.DefaultPackage?.Name}'");
// existing logic...
}
2. Inspect Pool State at Runtime
Query the ObjectPoolModule to count alive AssetObject instances during gameplay:
var pools = ObjectPoolModule.Instance.GetAllObjectPools();
foreach (var pool in pools)
{
Log.Info($"{pool.GetType().Name} contains {pool.Count} objects");
}
3. Profile Native Allocations
Use Unity's Profiler → Memory → UnityEngine.Object view to identify undisposed AssetHandle instances. Any valid handle that survives scene transitions indicates a leak in the wrapper or pool layer.
4. Validate Shutdown Sequences
Ensure ResourceModule.Shutdown() triggers during application quit or scene unloading:
private void OnApplicationQuit()
{
ModuleSystem.GetModule<IResourceModule>()?.Shutdown();
}
Implementing Fixes for Resource Leaks
Apply these targeted fixes to the TEngine source code to eliminate resource retention.
Safe Asset Loading Patterns
Wrap every AssetHandle acquisition with try-finally blocks to guarantee disposal when loading fails or is cancelled:
public async UniTask<T> LoadAssetAsync<T>(string address) where T : UnityEngine.Object
{
var handle = YooAssets.LoadAssetAsync<T>(address);
try
{
await handle.Task;
return handle.AssetObject;
}
finally
{
if (handle.IsValid) handle.Dispose();
}
}
Pool Cleanup on Shutdown
Extend ObjectPoolModule to clear all pooled objects during module shutdown:
internal sealed partial class ObjectPoolModule : Module, IObjectPoolModule, IUpdateModule
{
public override void Shutdown()
{
foreach (var kv in _objectPools)
{
kv.Value.Clear(); // releases every pooled object
}
_objectPools.Clear();
base.Shutdown();
}
}
Module Shutdown Extensions
Force release all unmanaged assets in ResourceModule.Shutdown by iterating the internal asset map:
public override void Shutdown()
{
foreach (var kv in _assetInfoMap)
{
var handle = kv.Value.AssetHandle as AssetHandle;
if (handle != null && handle.IsValid)
handle.Dispose();
}
_assetInfoMap.Clear();
base.Shutdown();
}
AssetObject Release Guard
Modify AssetObject.Release in ResourceModule.AssetObject.cs to always dispose handles regardless of shutdown state:
protected internal override void Release(bool isShutdown)
{
var handle = _assetHandle;
if (handle != null && handle.IsValid)
handle.Dispose();
_assetHandle = null;
}
Verification Checklist
Confirm leak elimination using this validation sequence:
- Run the Unity Editor with Development Build and Script Debugging enabled
- Trigger pool dumps before and after scene load/unload cycles
- Verify
AssetObjectpool counts drop to zero after unloading - Confirm the Profiler shows no lingering
AssetHandlenative allocations - Test forced download cancellation and verify disposal logs appear
Summary
- Resource leaks in TEngine stem from undisposed
AssetHandleobjects and unreleasedAssetObjectpool wrappers - Debug leaks by adding logging to
AssetObject.Release, inspectingObjectPoolModulecounts, and profiling native memory - Fix leaks by implementing try-finally disposal patterns, clearing object pools in
Shutdown, and forcing handle disposal inResourceModule.Shutdown - Key files to modify include
ResourceModule.cs,ResourceModule.AssetObject.cs, andObjectPoolModule.cs - Always validate fixes using Unity's Memory Profiler and runtime pool inspection
Frequently Asked Questions
How do I check if my TEngine project has resource leaks?
Monitor the AssetObject pool count using ObjectPoolModule.Instance.GetAllObjectPools() and check Unity's Memory Profiler for persistent AssetHandle instances after unloading scenes. If counts increase with each scene load without decreasing on unload, leaks are present.
Why does TEngine not automatically clear object pools on shutdown?
The default ObjectPoolModule implementation focuses on runtime recycling performance rather than lifecycle management. The module implements IUpdateModule but lacks an explicit Shutdown override to clear its internal _objectPools dictionary, requiring manual implementation as shown in the fixes above.
What is the difference between AssetObject and AssetHandle?
AssetHandle is a YooAsset-native reference to a loaded Unity asset, while AssetObject is a TEngine wrapper class that inherits from ObjectBase and enables pooling. The wrapper manages the handle's lifetime but can leak if the pool never calls Release or if Release skips disposal during shutdown.
How do I prevent leaks when cancelling async asset loads?
Use try-finally blocks around all YooAssets.LoadAssetAsync calls to ensure handle.Dispose() executes even when the operation is cancelled or throws an exception. This prevents the AssetHandle from remaining valid in memory when the async operation aborts before completion.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →