Pathfinding Service causes excessive memory usage

We have been working on a defense game that includes waves of enemies with pathfinding. During playtest we noticed that the server memory usage immediately went up to 3,500 MB minimum and 4,600 MB maximum. Upon checking we found out that the AI scripts were actually the main cause of the excessive memory consumption.

The solution we tried was to create the path instance only once, expecting that it would be enough to reduce memory consumption.

Is it true that the pathfinding service can really cause memory leaks? If so, do you have any suggestions to avoid or minimize such leaks caused by the pathfinding service?

Any help is appreciated.

local wp do
	local success = pcall(function() return Path:ComputeAsync(Torso.Position, Destination) end)

	if success and Path.Status == Enum.PathStatus.Success and Path:GetWaypoints() > 2 then
		wp = Path:GetWaypoints()[3]
	end
	
	CurrentWaypoint = wp
end

Of course it will cause excessive of memory usage. Your using ComputeAsync per waypoint reached which is alot.

With this code, All the memory usage is in untracked memory which never gets garbage collected.
I am the friend of OP who works with me personally. We’ve tried the following solutions.
Reducing the interval of which the path is computed.
Destroying the path immediately after it is computed.
None of these solutions actually solved it and only served as bandaids.

The real code looks like this.

local m, e, k, z = script.Parent, script.Parent.Name, script.Parent:FindFirstChild("Head"), script.Parent:FindFirstChildOfClass("Humanoid") do
while k.Parent == script.Parent and z:GetState() ~= Enum.HumanoidStateType.Dead do

               local wp do
            if n and (n.Position- k.Position).magnitude > 20 then
                if temp < 5 then
                    if did then
                        if temp2 <= 0 then
                            --print("back to do")

                            did = false
                        end
                    else
                        local st, pth = pcall(function() return pathfindingService:FindPathAsync(k.Position,n.Position) end)

                        if st and pth and pth.Status == Enum.PathStatus.Success then
                            local wps = pth:GetWaypoints()

                            if table.getn(wps) > 0 then
                                wp = wps[math.min(table.getn(wps), math.floor(z.WalkSpeed/5))] --set this to 5 if your npc is fast, to avoid jittering

                                --workspace.Effect.Position = wp.Position
                            end
                        end
                    end
                else
                    --print("blocked")

                    did = true
                    temp2 = 5
                end
            end
        end


        --if wp then
        --    print("by wp")
        --end


        temp = lp == floorVec3(wp and wp.Position or l) and temp + 1 or 0

        if active.Value then
            ls = true
            z:MoveTo(wp and wp.Position or l, n)
        elseif not wp and ls then
            ls = false
            z:MoveTo(script.Parent.HumanoidRootPart.Position)
        end

        if wp and wp.Action == Enum.PathWaypointAction.Jump then
            z.Jump = true
        end
        --z:MoveTo(l, a)

        d = p
        lp = floorVec3(wp and wp.Position or l)
        task.wait(wp and f/20 or f/5)
    end
end

The code is really hard to read and thats alot of nests

You can ignore some of the variables here since they are not that important, but I think the rest is actually easy to read if you have a healthy attention span but i’ll explain it to you.
This is a while loop that creates a path instance every .05 seconds assuming the NPC’s walkspeed is 16, the f Variable is supposed to be 17 divided by the NPC’s Walkspeed if the target is more than 20 studs away from the NPC. else it will follow the target every .2 seconds.