reckfellENGINE

Finding leaks: engine objects and C# memory

Distinguish native object counts, detached entities and managed memory.

On this page

Counters answer different questions

MetricMeaning
ObjectCountNative objects registered in ObjectDB
ObjectNodeCountEntities inside the active EntityTree; the exact SDK constant contains Node
ObjectOrphanNodeCountEntities outside the active tree, including children of detached hierarchies; debug builds
GC.GetTotalMemory(false)Approximate .NET managed memory rather than whole-process memory

Read the first three metrics through Performance.GetMonitor with the corresponding Performance.Monitor value.

ObjectOrphanNodeCount does not mean only parentless objects: a detached root's child is outside the tree too. Without DEBUG_ENABLED this monitor returns 0, so zero in such a build does not establish that these objects are absent.

Do not rely on an ObjectDB Profiler tab as the only solution: a snapshot UI of that name is not certified in this distribution. Available counters are not retaining-reference graphs and do not account for every native buffer, GPU allocation or managed array.

Detachment, deletion and managed references

ObjectLifetimeProbe.cs
C#
using System;
using System.Collections.Generic;
using Reckfell;

public static class ObjectLifetimeProbe
{
    public static bool Run()
    {
        var retained = new List<Entity>();
        var parent = new Entity();
        var child = new Entity();
        retained.Add(child);
        parent.AddChild(child);
        parent.RemoveChild(child);
        parent.Free();
        bool detachedSurvived = ReckfellObject.IsInstanceValid(child);
        child.Free();
        bool wrapperOutlivedNative = retained[0] is not null &&
            !ReckfellObject.IsInstanceValid(retained[0]);
        retained.Clear();
        return detachedSurvived && wrapperOutlivedNative;
    }

    public static void PrintSnapshot()
    {
        double objects = Performance.GetMonitor(Performance.Monitor.ObjectCount);
        double entities = Performance.GetMonitor(Performance.Monitor.ObjectNodeCount);
        double orphans = Performance.GetMonitor(Performance.Monitor.ObjectOrphanNodeCount);
        long managed = GC.GetTotalMemory(false);
        ReckfellApi.Print($"MEMORY objects={objects}; entitiesInTree={entities}; outsideTree={orphans}; managedApproxBytes={managed}");
    }
}

Run returns true: RemoveChild leaves the child alive, and freeing the parent still leaves that detached child alive. Freeing the child then deletes its native object while List retains its managed wrapper. A non-null reference alone therefore does not establish native validity.

An attached child is freed with its parent. Application logic must arrange cleanup for separately retained detached objects. With QueueFree, wait for deferred deletion before comparing state.

Establish growth before assigning a cause

Repeat one scenario: open a level, create objects, close the level and wait for queued deletion. Compare the same point across several cycles. Initial growth may be caching or initialization; stable object counts do not prove stable internal buffer sizes.

An ordinary new byte[8192] increases managed allocations without adding an ObjectDB object. Track your own created objects when identifying missing deletion; managed retaining paths require a separate .NET memory tool. One ObjectCount value cannot fully diagnose a leak.

Run the example · Deletion responsibility

Reckfell Engine documentationBack to top ↑
QUICK LINKSGetting startedEntity hierarchyC# & .NET 10Scenes and prefabs
Reckfell guides and full API K
reckfellENGINE
RECKFELL ACCOUNT

Sign in

Sign in to your Reckfell account.

One account for the entire Reckfell platform.