I've been trying to catch memory leaks, and while godot is helpful with orphan nodes, I'm a bit confused about what the object count means compared to overall node count when monitoring nodes.

I have a very simple random test wave that has two forms:

The first is one that has a tween or bezier curve that crosses the screen. When the tween or curve ends, the spawned node should free itself from memory.
The second is one that has a tween or bezier curve that crosses the screen, but they self destruct midway through the screen.

Both should free themselves, but I seem to get much higher object count with the self destruction function.

Crossing screen

Self destruct

To keep in line with good practice, I chose to directly base the bezier function on the one in the documentation:
if t <1:
t += speed * delta
position = _cubic_bezier(p00, p01, p02, p03, t)
if t >= 1 && curveno == 1:
if freeaftercurve == true:
queue_free()

When the wave is set to self destruct I have the result of the explosion as this function, with the "shotcontainer" being a child node that spawns a bullet, and the explosion node being sent to the specified layer in the main scene, as to place it over the action, but under the HUD:

func suicidegunsing():
$CollisionShape2D.set_deferred("disabled", true)
passupdeath()
explode2 = preload("res://Core/PARTICLEHANDLING/explosioncont.tscn").instantiate()
explode2.position = position
speed = 0
get_tree().root.get_node(GlobalUnitLoadout.enemyexplosionlayer).add_child(explode2)
shotcontainer.startpos = position
shotcontainer.vectorx = 0
shotcontainer.vectory = 0
#animed?
shotcontainer.pureangle = 0
shotcontainer.homing = true
#generics
shotcontainer.bulletname = "Small"
shotcontainer.speed = 200*randf_range(.5,1)
shotcontainer.actionname = "SingSui"
if makesuresuicideworks == false:
makesuresuicideworks = true
await get_tree().create_timer(.5,false).timeout
$Sprite2D.hide()
queue_free()

Since the shotcontainer is freed with the body, and the bullet it spawns frees itself on leaving the screen, the explosion itself is the only other thing I think need to show:

func _ready():
await get_tree().create_timer(.1).timeout
position.x += randf_range(-10,10)
position.y += randf_range(-10,10)
await get_tree().create_timer(1,false).timeout
queue_free()

and this is just two sets of particle effects, with the timer set to their spawn rate.

The gist of it is that I want to pass up, through a universal script, the path to the necessary layers for nodes in the main scene, since I always want the layer the explosion exists on to be higher than the body. Maybe that the explosion layer I want to spawn that node in seems to leak due to bad code. and I'm approaching autoloaders incorrectly?

I see a pretty exponential growth in object count despite the node count remaining roughly the same, and I'm not sure why. I really only have front end dev experience, so if this is just an issue with how I set up the function for self destruct (and even just some typo), then I'm fine with that. Maybe there's an issue in how I set it up, but I'm not sure how to approach it properly in the context of godot's node system, and it's hard to really figure it out without asking.

All nodes are objects. But not all objects are nodes. There are for example resources. If you open the in-editor help about object you will see a long list of classes derived from the object base class.

Okay, I understand then. That helped me track down what I see as the cause. The explosion particle fx causes the high rise in object count, but I'm not sure how to deal with that because while other objects free themselves from memory when their nodes/scenes are freed, the objects related to the particles seem to remain in memory even when their scene/nodes are freed.

I'll recheck the docs to see how to free them.