On running the profiler I can see that the Objects count in Monitors keeps increasing due to a specific function call. Is it possible to check which specific class objects are increasing in count here?

Might be unrelated but I'll describe the scenario if it helps:

  • I'm working on implementing an Astar pathfinding library for 2D platformers. I have a tilemap for the level layout. There can be multiple agents that will traverse this tilemap
  • I'm scanning the tilemap to generate a 2D array (using a manually created 2D array class). This will be common for the multiple agents using the pathfinding logic. Common because it will store the coordinates and metadata about the tile which might be needed for the pathfinding logic later.
  • Each agent will have it's own Pathfinding class object. Each agent will have it's own Astar nodes in this pathfinding object. This way if a tile is common for two agents, the Astar node F cost can be different for both of them on the same map location.
  • The pathfinding Astar nodes are being stored in an Array2D. I have added extends RefCounted at the top of the Astar node class, assuming Godot will remove these objects when these are no longer stored in the Array2D. I also tried adding queue_free when changing any Astar node value in the Array2D but the memory leak is still there.
  • The pathfinding function is being called by the agents in _process. There will be a parameter to throttle this for performance but I'm testing it out without that first.
  • Zini replied to this.
  • The NodeAstar objects do reference each other in previous_node and the neighbors array.

    This is likely your problem. Sounds like you are creating cyclic references. RefCounted can't deal with that AFAIK.

    There is a WeakRef class that is supposed to be used together with RefCounted. But I have never used it and can't give you any advice about it.

    Alternatively you could switch from RefCounted to Object. In this case you would have to keep track of all your objects yourself and free them when no longer needed. This is basically manual memory management.

    spawd Adding extends RefCounted to a node script shouldn't even work. RefCounted and Node are two different things.

    Creating a memory leak in Godot is quite difficult as long as you queue_free all your nodes and don't work with bare objects.

    If a specific function seems to cause the increase in object count, maybe post it here so that other people can have a look at it?

      Zini Sorry for the confusion. By Astar node I don't mean the Godot node but the data structure "node" that the Astar algorithm uses. I'm calling it Astar node because that seems to be the general nomenclature when talking about Astar

      Here's the relevant code:

      • In pathfinding.gd I'm creating nodes with

        neighbor_node = NodeAstar.new({"tile": neighbor_tile})
      • This is the NodeAstar class (with only the relevant code to the issue)

        extends RefCounted
        
        func _init(properties):
            tile = properties.tile
        
        ## Previous node which connected to this node
        var previous_node: NodeAstar
        
        var neighbors: Array[NodeAstar] = []
      • The nodes are stored in an array2D. Relevant code below:

        extends RefCounted
        
        var grid: Array = []
        var grid_width: int
        var grid_height: int
        
        func _init(grid_width, grid_height):
            self.grid_width = grid_width
            self.grid_height = grid_height
            for i in grid_width:
                var column_array = []
                for j in grid_height:
                    column_array.append(null)
                grid.append(column_array)
        
        func reset_values(reset_value):
            for x in grid_width:
                var column_array = []
                for y in grid_height:
                    grid[x][y] = reset_value;
      • And I'm calling the reset_values method before running the pathfinding function

        var node_grid: Array2d
        
        # reset is called before the pathfinding function. This will set all values of node_grid to null
        node_grid.reset_values(null)
      • I realize setting all values to null might not be the best approach here but I want to understand why there is a memory leak. The NodeAstar objects do reference each other in previous_node and the neighbors array. Is it because of that?

      If I change NodeAstar to extends Node, and then call queue_free in reset_values then the memory leak issue is resolved.
      Should I go ahead with using Node in this scenario without adding them to the scene tree?

      The NodeAstar objects do reference each other in previous_node and the neighbors array.

      This is likely your problem. Sounds like you are creating cyclic references. RefCounted can't deal with that AFAIK.

      There is a WeakRef class that is supposed to be used together with RefCounted. But I have never used it and can't give you any advice about it.

      Alternatively you could switch from RefCounted to Object. In this case you would have to keep track of all your objects yourself and free them when no longer needed. This is basically manual memory management.

        Zini Got it, thanks for the clarification. I'll go ahead with using Node and calling queue_free then.

        I tried using Object instead of RefCounted but calling the free command gave me an error. Something along the lines of cant free a reference. Assuming it's talking about the cyclic references here. Not 100% sure though, will look this up further