Do I need to free/dispose objects created by Godot (not by me)?

Godot Version

4.5

Question

According to the docs for the C# edition of Godot, I should free my objects to release their unmanaged counterparts, by calling Free() or QueueFree(). That’s okay — I’m an experienced .NET developer, so I understand the concept and the reasons behind it.

However, I couldn’t find enough information about what the engine frees/disposes automatically, and when. For example, consider the following code:

public partial class MyNode : Node2D
{
    
    [Export]
    public StringName? MyAnimationName { get; set; }

}

In the code above, will MyAnimationName be disposed by the engine automatically, or am I required to dispose it manually? Since I was not the one creating the StringName object, it makes sense to me that I should not be responsible for its disposal, but I can’t find info about what actually happens.

By the way, I’m referring to freeing and also to disposal, as the concept of releasing the object’s resources. StringName, for example, has no Free() method, but it implements IDisposable, for what I read.

Just use QueueFree() when you’re done with an object and don’t worry about it too much. For example, when an enemy dies. (Unless you’re pooling them in a bullet hell game for instance.)

I recommend you read more about RefCounted if you want to understand more about memory management.

Note: In C#, reference-counted objects will not be freed instantly after they are no longer in use. Instead, garbage collection will run periodically and will free reference-counted objects that are no longer in use. This means that unused ones will remain in memory for a while before being removed.

Objects like Resource inherit from that and are automatically garbage collected when nothing is using them.

But Godot is a high-level language like C# and does the memory management for you. It is possible to create a memory leak with Godot, but that usually happens when you don’t add objects to the tree and lose reference to them so they don’t get freed when you’re done with them.

That depends on the rest of the code. You declared a partial class, which would mean there’s more code defining this class. So what does it do?

However I can tell you that MyAnimationName is a variable that is part of MyNode, So Godot won’t do anything with it. MyNode inherits Node2D < CanvasItem < Node < Object. It is not a RefCounted object. Which means it will not be garbage collected until you call QueueFree() on it. You do not need to worry about the garbage collection of its member variables like MyAnimationName. If you were to try to free member variables while a Node is in use you are opening yourself up to an execution error.

If you wanted to free up MyAnimationName while MyNode was in use, make it a Resource that contains a StringName and then null out the value when you’re done with it. Then your Resource will garbage collect itself if nothing else is referencing it.

3 Likes

Also useful for debugging, to use get_orphan_node_ids() and print_orphan_nodes() to check any Object or nodes that outside of SceneTree but forgot to be freed. C# equivalent would be GetOrphanNodeIds() and PrintOrphanNodes() I assume. Only works in debug builds.

3 Likes

Any Godot object that has RefCounted in its upstream class hierarchy is managed and you don’t need to worry about it. This includes all resource types. Any object that doesn’t, and that includes all node types, if you create it, you’re responsible to free it. The only exception is nodes parented to a node that you free. They will all automatically be freed as well.

2 Likes

Thank you for the detailed reply!

I’ve read about RefCounted in the docs, and about Object (not System.Object) not being a RefCounted. RefCounted seems much like a managed object, and I wish Object (and therefore Node) were all RefCounted, as it would make things much easier to follow for me.

But, anyway, my concern was about who has the responsibility for releasing stuff in certain situations. In the .NET world, it is almost like a ritual to always call Dispose() on stuff which lifetime you are responsible for. And I was wondering if I should be disposing stuff that is borrowed to me by the engine (like values set in the inspector) or if the engine is the one that takes that responsibility. I try not to worry too much about it, but we’ve all been bitten by memory leaks at some point in our dev lives, and hence I would feel safer to always know about the lifetime of the objects I use.

I’m always careful when dealing with IDisposables, and I’m not used to rely on garbage collection for their disposal, since it is not uncommon for an IDisposable instance to be referenced in some internal collection somewhere until it is disposed, preventing the garbage collector from ever collecting it. And that’s why I decided to reach out to you guys, since it’s better safe than sorry.

Godot by design minimizes user responsibilities. The majority of objects you typical create in script code will be RefCounted i.e. they will be memory managed via reference counting. C# garbage collector probably just sits on top of that mechanism.

Again, you can only leak objects that you explicitly construct whose classes are not reference counted. This will in 99% of cases be nodes. Godot’s built in profiler/monitor keeps track of orphaned nodes. So even if you make a leak, it won’t be sneaky.

I suggest you play a bit with object creation and (non) destruction and observe what happens in engine’s orphan node monitor.

As for the inspector, the basic types are all passed/assigned by value so those are of no concern. Everything else you can create in the inspector is either an array, a dictionary or one of many resource types. Those are all reference counted.

So for example if you create/assign a material in the inspector, a new material resource object will be created. If you then again create a new material for that same property, the new material will be assigned while the old one just goes into oblivion. Its reference count falls to zero and the object is sooner or later destroyed under the hood.

Exactly the same happens if you do that via script
It’s all neatly throwaway.

2 Likes

That is very pleasing! And, for what I’m learning from you guys, is that I don’t need to worry too much about it, because Godot is mostly managed-like (with its RefCounted derived types). I’m a newcomer to Godot, and I’m liking it. Now, I like it a bit more.

I just wonder what are the design decisions behind not making Object itself not reference counted as well. I guess there is a very good reason, though.

Also related to object freeing, I’ve read that I should check IsInstanceValid() to make sure it is accessible. Because of that, I made an extension method similar to one I found on the web, that returns return node is not null && node.IsInstanceValid();. I’m doing this check on most of my methods, before accessing a node, but I don’t really know if that is really necessary. Would you like to share your insights on this?

You’d have to ask the Godot team about that decision. We can only guess.

I would ask where you read that. Any advice that was written/given before 4.4.1 could be misleading.

You of course can do those checks if you want, but you’ll find that you don’t really need them. They won’t actually help you solve as many problems as you think. A Node can be valid and not null and also not ready; and so will pass those checks, then fail when you try to access it before it is ready.

When you would like to check if a Node is valid before doing something to it (like with an optional value), there’s a much easier way to do those checks. So let’s say that my level scene has a music exported value. If I assign it, I want the game music to change. If I don’t, I want the current music to keep playing.

class_name MyLevel extends Node2D

@export var level_music: AudioStream

func load_level() -> void()
	if level_music:
		Music.play(level_music)

If the music is null, or the instance is invalid (which is unlikely) the music just won’t switch. If it is, play it. (In this example I’m calling a Music autoload singleton I created outside the script.)

1 Like

I think the reason is nodes. Their primary purpose is to sit in the scene tree so in majority of use cases there will always be at least one reference to a node. It’s borderline redundant. The other not less important reason may be performance. Nodes are the main thing that is processed en masse on a frame-to-frame basis. Cramming some pedestrian reference counting in the midst of it would just bog it down. Memory management is for spoiled user scripts, not for central performance critical functionality :wink:

If you look in the docs what inherits Object, you’ll notice it’s mostly engine’s core singletons, some obscure semi-internal data structures and… Node. Most of that, except Node, you can’t or won’t ever instantiate on your own.

2 Likes