Enemy AI causes giant memory leakages and lag

So i just found out the other day that the AI of enemies in a game i’m trying to make is causing memory leakages and lag. These memory leakages would appear as spikes in memory instead of gradual increases in memory. I think i need help with solving this with someone in studio because this problem is so hard for me to fix let alone completely find.

I’ve tried reducing the interval of which a path is computed.
And the lag also occurs even when less than 20 enemies are present. in fact the game had lag spikes like this when there was just 1 - 11 enemies on the map. i don’t know why this is happening but it’s not fun to deal with.
The npcs pathfinding would also have trouble using their pathfinding as they would often be ramming towards a wall, which i think is a result of the lag.

I’ll show code snippets here

local function Main(Position, Part:BasePart)
	if Position then
		local Mag = (Root.Position - Position).Magnitude
		if Mag > Range.Value or Part and not CheckSight(Part) then
			Path = PathfindingService:CreatePath(table.unpack(PathParams))
			local Success = pcall(function() Path:ComputeAsync(Root.Position, Position)  end) -- the current assumption is that waypoints are causing high memory usage
			local CurrentIndex = 0

			if Success and Path and Path.Status == Enum.PathStatus.Success then
				Path.Blocked:Connect(function(BlockedIndex) -- Make this a one time connection from what i figured
					if BlockedIndex >= CurrentIndex then
						Blocked = true
					end 
				end)
				local Points:{PathWaypoint} = Path:GetWaypoints()
				if DebugMode then
					DebugPath(Points)
				end
				task.spawn(function()
					for i, v in Points do
						if not Active.Value or Humanoid:GetState() == Enum.HumanoidStateType.Dead or not Path then
							break
						end
						Waypoint = v
						CurrentIndex = i
						task.wait((16/Humanoid.WalkSpeed)/4)
					end
				end)
			else
				print("Path creation unsuccessful")
			end
		end
	end
end

local function Clean()
	Waypoint = nil
	for i, v in VisibleWaypoints do
		v:Destroy()
	end
	table.clear(VisibleWaypoints)
	if Path then if Path then Path:Destroy() end end
	Path = nil
end
task.spawn(function() -- Get Target
	while task.wait(.2) and Humanoid:GetState() ~= Enum.HumanoidStateType.Dead do
		local Check = GetNearestTarget(Docile and Range.Value or math.huge, Root.Position, false)
		TargetToMoveTo = Check ~= nil and Check or TargetToMoveTo
	end
end)

task.spawn(function() -- Make Path
	repeat
		if TargetToMoveTo and not CheckSight(TargetToMoveTo) then
			--if Main() then
			Main(TargetToMoveTo.Position, TargetToMoveTo)
			local Waits = 0
			local Sight
			repeat
				Sight = CheckSight(TargetToMoveTo)
				Waits += task.wait(.2)
			until Waits >= 2 or Blocked or Sight
			if Path then
				if Path then Path:Destroy() end -- Destroy it every 2 seconds and create a new one
				Path = nil	
				Waypoint = nil
			end
			--end
		end
		task.wait(.2)
	until Humanoid:GetState() == Enum.HumanoidStateType.Dead
end)

Humanoid.Died:Connect(Clean)
Character.Destroying:Connect(Clean)
repeat
	if not Active.Value then
		Humanoid:MoveTo(Root.Position)
		Active.Changed:Wait()
	end
	local Interval = (16/Humanoid.WalkSpeed)/4
	if TargetToMoveTo then
		local Magnitude = (TargetToMoveTo.Position - Root.Position).Magnitude
		local Height = TargetToMoveTo and (TargetToMoveTo.Position.Y - Root.Position.Y)
		local MoveMethod = TargetToMoveTo.Position
		if Mode.Value == 2 then
			if Magnitude < StopAt.Value then
				MoveMethod = Root.Position
			end 
		elseif Mode.Value == 3 then
			if Magnitude < StopAt.Value and Magnitude > StopAt.Value/4 then
				MoveMethod =  TargetToMoveTo.Position
					+(Vector3.new(math.random()-.5,0,math.random()-.5).Unit
						*math.abs(math.min((TargetToMoveTo.Position-Root.Position).Magnitude-Range.Value,0)))
				Interval = math.random(1, 2) / 2
			elseif Magnitude < StopAt.Value/4 then
				MoveMethod = CFrame.new(TargetToMoveTo.Velocity):Inverse().Position				
			end
			if Height > HeightLimit then
				if not CheckSight(TargetToMoveTo, Range.Value + Height * 2) then
					MoveMethod = TargetToMoveTo.Position + (Vector3.new(math.random(-1, 1) * Range.Value, 0, math.random(-1, 1) * Range.Value) * (TargetToMoveTo.Position - Root.Position).Magnitude) * Height
					Interval = math.random(1, 3) / 2
				else
					MoveMethod = Root.Position
				end
			end
		end
		if Waypoint and Waypoint.Action == Enum.PathWaypointAction.Jump or Height >= HeightLimit/2 and Height < HeightLimit * 2 then
			Humanoid.Jump = true
		end
		Humanoid:MoveTo(Waypoint and Waypoint.Position or MoveMethod, TargetToMoveTo)
		GETOUTTAMYWAY()
	end
	task.wait(Interval)
until Humanoid:GetState() == Enum.HumanoidStateType.Dead

And these images are the proof
The memory leakages present as spikes that do not go down instead of the typical gradual memory leakage.
The lag was persistent even with very few npcs



It appears your script is constantly deleting paths and then creating new ones, which could lead to a possible memory leak. Maybe try doing something like this instead.

local Path = nil  --\\ Move the declaration outside the loop

task.spawn(function()
    repeat
        if TargetToMoveTo and not CheckSight(TargetToMoveTo) then --\\ Checks if not in sight and checks for target to move to
            Main(TargetToMoveTo.Position, TargetToMoveTo)
            local Waits = 0
            local Sight
            repeat
                Sight = CheckSight(TargetToMoveTo)
                Waits += task.wait(.2)
            until Waits >= 2 or Blocked or Sight
            if Path then
                --Path:Destroy()
                Path = nil
                Waypoint = nil
            end
        end
        task.wait(.2)
    until Humanoid:GetState() == Enum.HumanoidStateType.Dead
end)

If this doesn’t help, just reply letting me know!

I destroy paths because i thought that it would gc after i dereference it
that’s what you’re supposed to do with instances after all so i doubt i can just dereference it and not destroy it, i don’t trust that

and plus since the paths are dereferenced first and then the reference is now the new path, it’s like replacing a old path with a new one so i thought things would be perfectly fine
also how did you find the exact cause of that problem?

also i forgot to mention that the path declaration is already outside the loop, i’ll send a link to the model for the ai here

so i tried your solution and it didn’t work
memory leaks and lag are still present

I think you can calculate a path globally, instead of a unique one for every single NPC. Haven’t tried this myself, though. If you aren’t worried about the map undergoing changes mid-game, you could calculate the path only once to save on resources.

it has to be unique because the enemies might have to change paths to find a new target
calculating it only once sounds like a bad idea really

Sorry if I wasn’t clear in what I said.

:ComputeAsync() is the function that returns waypoints between two points, while :CreatePath() is what generates the ‘map’ used for pathfinding. So you can call ComputeAsync() on the same path object multiple times, saving tons of resources.

i think i’ve tried that before on an older ai, i’ll try it again because now every ai in my game uses nearly the same source code so i’ll see if it works

so i tried it on my ai and the memory leaks and lag still kept occuring

It doesn’t seem to go anywhere near as high as in the previous screenshots, does it still go up to 5000+ after waiting a while?

Also, have you tested it with 0 npcs to make sure there’s no other memory leaks elsewhere?

it going up to 5000 is rare but it can happen after a long period of time, it spikes to that suddenly without warning or time to react

I have made a test where all npcs would have their ai disabled and i found no memory leakages elsewhere
and then i turned their ai back on and the memory went up immediately to 1,200 from 900 - 1,000

Hmm. Does it go back down if the NPCs are destroyed?

It seems the earlier link to the model is now a 404, so I can’t analyze your code in full.

1 Like

no it doesn’t go down if the npcs die or get destroyed
i’ll update the model immediately so here link

link updated yesterday
please check it