# How to Debug and Fix Resource Leaks in a TEngine Project: A Complete Guide

> Fix TEngine resource leaks with practical solutions. Learn to implement disposal guards, clear pools, and use try-finally blocks for async asset loading in your TEngine project.

- Repository: [ALEX/tengine](https://github.com/alex-rachel/tengine)
- Tags: how-to-guide
- Published: 2026-02-24

---

**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`](https://github.com/alex-rachel/tengine/blob/main/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`](https://github.com/alex-rachel/tengine/blob/main/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`](https://github.com/alex-rachel/tengine/blob/main/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:

```csharp
// 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:

```csharp
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:

```csharp
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:

```csharp
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:

```csharp
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:

```csharp
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`](https://github.com/alex-rachel/tengine/blob/main/ResourceModule.AssetObject.cs) to always dispose handles regardless of shutdown state:

```csharp
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:

1. Run the Unity Editor with **Development Build** and **Script Debugging** enabled
2. Trigger pool dumps before and after scene load/unload cycles
3. Verify `AssetObject` pool counts drop to zero after unloading
4. Confirm the Profiler shows no lingering `AssetHandle` native allocations
5. Test forced download cancellation and verify disposal logs appear

## Summary

- **Resource leaks** in TEngine stem from undisposed `AssetHandle` objects and unreleased `AssetObject` pool wrappers
- **Debug leaks** by adding logging to `AssetObject.Release`, inspecting `ObjectPoolModule` counts, and profiling native memory
- **Fix leaks** by implementing try-finally disposal patterns, clearing object pools in `Shutdown`, and forcing handle disposal in `ResourceModule.Shutdown`
- **Key files** to modify include [`ResourceModule.cs`](https://github.com/alex-rachel/tengine/blob/main/ResourceModule.cs), [`ResourceModule.AssetObject.cs`](https://github.com/alex-rachel/tengine/blob/main/ResourceModule.AssetObject.cs), and [`ObjectPoolModule.cs`](https://github.com/alex-rachel/tengine/blob/main/ObjectPoolModule.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.