Godot: Tool to show stray nodes and resources from scene tree.

Created on 29 May 2017  Â·  21Comments  Â·  Source: godotengine/godot

Operating system or device - Godot version: Windows 8.1 - Godot 2.1.x

Issue description:
When a variable is loaded via load("res://some_script.gd").new(), or SomeObject.new() it is not automatically freed when quitting the application, and there is an error message when closing the game window :

ERROR: SelfList<class GDFunction>::List::~List: Condition ' _first != 0 ' is true.
   At: core\self_list.h:80
ERROR: SelfList<class GDScript>::List::~List: Condition ' _first != 0 ' is true.

   At: core\self_list.h:80
WARNING: ObjectDB::cleanup: ObjectDB Instances still exist!
     At: core\object.cpp:1845
ERROR: ResourceCache::clear: Resources Still in use at Exit!
   At: core\resource.cpp:378

or:

WARNING: ObjectDB::cleanup: ObjectDB Instances still exist!
     At: core\object.cpp:1845

godot bug

Steps to reproduce:

  1. Create a scene with a root Node and attach a script
  2. Write this piece of code in the scene's script:
extends Node

var object_from_script
var other_object

func _ready():
    object_from_script = load("res://some_script.gd").new()
    other_object = Node.new()

To try the bug with object_from_script, you need to create the script first, you can keep the default code in it. To test the two errors (see image), comment one of the two lines in the _ready() function.

  1. Launch and quit the game (do NOT stop the scene from the editor)

How to avoid the error until it is fixed:
The only way I found to avoid the problem in my code is to add this piece of code:

 func _exit_tree():
    object_from_script.free()
    other_object.free()

As objects created from the editors are automatically freed, I think this is a real bug.
If it is a wanted behaviour, I'd like to know a less redondant way to avoid it ;)

Link to minimal example project:
Here is the test project I used:
bug report.zip

bug core

Most helpful comment

We could try showing the number of unparented nodes in the debugger

On May 30, 2017 10:51 AM, "Matthieu Huvé" notifications@github.com wrote:

Thank you all for your answers!
I guess this issue can be closed now, as it is not a bug, but a mistake as
was making.

We can keep it open for eon-s's suggestion, or create it's own suggestion
thread ?

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/godotengine/godot/issues/8985#issuecomment-304884595,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AF-Z25Ioznt6Ue9KBq62snYD9cdXQHaHks5r_B7sgaJpZM4NpjQ3
.

All 21 comments

is not should be handled manually?
what about assigning instance out of class?

Node A

extends Node

var object_from_script
var other_object

func _ready():
    pass

Node B

extends Node

var instance_a
var instance_b
var node_a

func _ready():
    instance_a = load("res://some_script.gd").new()
    instance_b = Node.new()

    node_a = preload("res://NodeA.tscn").instance()
    node_a.object_from_script = instance_a
    node_a.other_object = instance_b

    add_child(node_a)
    node_a.queue_free() # now instance_a & b should be freed automatically?

_edit code : NodeA.new() -> preload("res://NodeA.tscn").instance()_

I still get the same error.
However, I changed your code a bit and replaced

node_a = NodeA.new()

by

node_a = preload("res://NodeA.tscn").instance()

The main scene was NodeB.tscn

yes. you should be same error.
what I was trying to say is that automatically free() will cause problem in this case.

Ah OK, sorry ^^

However I don't see the real problem in your code...
instance_a and instance_b are defined in NodeB, and referenced in NodeA. When you free NodeA, you just remove the references but the original objects are still here.

Tell me if I'm wrong, but I think instance_a and instance_b should be automatically freed when NodeB is freed, as they are defined "inside" NodeB.

From here in the docs, I understand that objects are automatically deleted.
But from there, I understand the opposite... I am completely lost...

@MattUV one doc is from Reference and the other from Object.

In the case of References, I've thought they were removed when count was zero but got a leak while removing Nodes from the tree, so, that seems is not exactly how they work, docs need better explanation on Reference.


Better than what you are trying to do is to override _notification and check NOTIFICATION_PREDELETE and free all the Objects you need.

Sometimes you may want to exit tree and keep the variables, or to remove a node and keep some variables somewhere else.

Also check weakref that may be useful on some situations
http://docs.godotengine.org/en/stable/classes/class_weakref.html.

Lifetimes is not too complicated in GDScript, it just depends of the type:

  • Object Must be freed manually by calling free.
  • Reference: Freed automatically when the reference count reaches 0.
  • Node:

    • _Inside the scene tree_: Freed automatically by the parent. Optionally with queue_free.

    • _Not inside the scene tree (very rarely the case...)_: Same as Object. Must be freed manually by calling free.


About @eon-s doubt on Reference in GDScript:
The refcount is incremented on assign, pass as argument, etc. It's decremented...:
Members: when the instance is freed.
Locals: it's a bit weird in this case though. You would expect it to be on scope exit like in C++, but it's actually on function return. However, and this is the weird part, if there are two locals at the same stack address, the refcount is decremented when the latter local is first assigned. e.g.:

func hello_there():
    if whatever:
        var myref = Reference.new()
        print(myref)
    # myref is still alive here, after exiting its scope. it's not visible though
    return # the refcount for myref is decremented here, it's freed since it reaches 0

func oh_its_you():
    if whatever:
        var myref = Reference.new()
        print(myref)
    # myref is still alive here, after exiting its scope. it's not visible though
    if whatever:
        # myint is on the same stack address as myref,
        # therefore it replaces the Variant at that address.
        # the previous Variant at that stack address (the one for myref) dies,
        # so the refcount for myref is decremented here instead
        var myint = 10
        print(myint)
    return

I just checked your example project. You forgot to add the newly created nodes as children:

func _ready():
    object_from_script = load("res://some_script.gd").new()
        add_child(object_from_script)
    other_object = Node.new()
        add_child(other_object)

This way they are in the scene tree and the parent will free them automatically when freed.

If you don't do this, not only you must free them manually, but features like _process or _fixed_process won't be usable.

Here is a recommendation, always use add_child and let the parent control the lifetime of your node, unless you know exactly what you are doing.

Could it (on debug) try to be more precise on the instances and Resources that still exists? Like trying to show the type, ID and name if apply?

Thank you all for your answers!
I guess this issue can be closed now, as it is not a bug, but a mistake as was making.

We can keep it open for eon-s's suggestion, or create its own suggestion thread ?

We could try showing the number of unparented nodes in the debugger

On May 30, 2017 10:51 AM, "Matthieu Huvé" notifications@github.com wrote:

Thank you all for your answers!
I guess this issue can be closed now, as it is not a bug, but a mistake as
was making.

We can keep it open for eon-s's suggestion, or create it's own suggestion
thread ?

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/godotengine/godot/issues/8985#issuecomment-304884595,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AF-Z25Ioznt6Ue9KBq62snYD9cdXQHaHks5r_B7sgaJpZM4NpjQ3
.

if anyone wants to work on this... otherwise kicking to 3.1

@neikeq can u explain more on why it isnt getting deleted, if your script extends node and you instance it, what is it considered to be? a node an object or a reference?
if you do this:
func _process(delta): var object = load('res://script.gd').new()

if you dont add it as child of a node, and if its considered a reference shouldnt it be deleted automatically on runtime?

if u use the code above and check godot monitor it shows object count rising and never stops

Node doesn't inherit from Reference, so it won't behave like one.

In your example the node object will leak as nobody is destroying it (but if you would add it to the tree then that would destroy the node).

TL;DR: Nodes aren't References

I have fallen for this myself and in my mass of code, I can't find which reference is not being freed. It would definitely be useful if it could output or give an indication of where the problem is. Right now I need to go through my code bit by bit to try and work out what is left behind.

@matty if you run Godot with the -v flag (for verbose) it'll list which objects were not freed when you close the game. Note that you need to run the game with the flag, not the editor.

@vnen Found it with that thanks. When I am changing scenes I am simply removing the scene with _current_view.remove_child(scene_name) and then doing _current_view.add_child(scene_name).

For some reason, the entire scene that was previously removed gets leaked and all its resources. Does remove_child not free up the resources then?

Does remove_child not free up the resources then?

I wouldn't think so because that's generally used to remove it from one part of a tree only to add it to another part later.

@OvermindDL1 Ok thanks. My game is quite unique it is creating nodes dynamically all the time e.g I have a TileMap highlighter that is created & added/removed to the tree on mouse over tiles. When closing the app it is no longer in the tree so this also leaks.

Swapped it around to always be in the tree and use hide and show anyway.

I think probably some tool to check stray nodes and cyclic references should be created... but will kick this to 3.2 given we are feature frozen

The debugger's "Monitors" panel can now show orphan nodes:
Screenshot_20191126_130057

Was this page helpful?
0 / 5 - 0 ratings