# godot-kotlin-jvm binding leak PoC Repro project for [utopia-rise/godot-kotlin-jvm#941](https://github.com/utopia-rise/godot-kotlin-jvm/issues/941). Minimal reproduction for a JVM-side binding leak in godot-kotlin-jvm 0.16.2 (present since the MemoryManager rework, still present on master): every non-RefCounted `Object` freed at runtime leaves its entry in `MemoryManager.ObjectDB` forever. For Objects the binding holds a strong reference to the wrapper, so the JVM heap grows without bound while native memory stays flat. ## Run Requires a godot-kotlin-jvm 0.16.2-4.6.3 editor binary and a JDK 17+. ```bash ./gradlew build godot --headless --import . godot --headless --import . # run twice, first pass may bail early godot --headless --path . res://main.tscn ``` Output on 0.16.2-4.6.3 (macOS, headless): ``` [POC] freed=20000 nativeObjects=1472 jvmObjectDB=20039 [POC] LEAK CONFIRMED: ~18567 zombie bindings in MemoryManager.ObjectDB ``` All 20000 freed nodes are still in the JVM ObjectDB. How the node is freed does not matter: `free()` from Kotlin, `queueFree()`, or engine-side deletion all leak. ## Root cause `KotlinBindingManager::_instance_binding_free_callback` queues the dead object via `MemoryManager::queue_dead_object(obj)`, which reads `obj->get_instance_id()`. Godot calls instance-binding free callbacks at the very end of `Object::~Object`, after `ObjectDB::remove_instance(this); _instance_id = ObjectID();` (core/object/object.cpp, same order in every 4.x release since 4.3). The id is always the null ObjectID by then, so the JVM is told to remove `ObjectID(0)` instead of the real id and the real entry stays.