However as things get more intensive, the pattern changes direction and becomes negative (the initial pattern in the graph appears flat because the scale suddenly changed):
The memory categories are used in a ModuleScript (I will call “BadModule”) where most exported functions set and reset the memory category. There are some exceptions which don’t set it, but that’s because they’re small and bounded and not interesting. Memory category is also set during initialization so we can track stuff created then. There are two exported functions which are called every frame, one sets the memory category and the other does not. This bug is for client-side memory, since this module does not run on the server.
-- Requiring other modules here, before we set memory category.
local MEM_CAT = "BadModule"
-- Set memory category so stuff created during initialization gets tracked.
debug.setmemorycategory(MEM_CAT)
local State = {
-- Many members here corresponding to dynamic state the module uses.
-- Linked list sentinels, numbers, sorted tables, etc.
}
-- Lots of local functions, read-only variables and tables, etc. here
function Module.Something()
debug.setmemorycategory(MEM_CAT)
...
debug.resetmemorycategory()
end
function Module.RenderSteppedFirst()
-- Not setting memory category because we don't do much here.
...
end
function Module.ClientHeartbeatLast()
debug.setmemorycategory(MEM_CAT)
...
debug.resetmemorycategory()
end
debug.resetmemorycategory()
return Module
The ClientHeartbeat and RenderStepped functions get called every frame by a god script (of sorts) which controls the ordering of all such pieces of code:
RunService:BindToRenderStepped("BadModule", Enum.RenderPriority.First.Value-1, function()
-- Nothing is done before (in this event callback)
BadModule.RenderSteppedFirst()
end)
RunService.RenderStepped:Connect(function()
Module1.RenderStepped()
end)
RunService.Heartbeat:Connect(function()
Module1.ClientHeartbeat()
Module2.ClientHeartbeat()
BadModule.ClientHeartbeatLast()
-- Nothing is done after (in this event callback).
end)
Callbacks from remotes and user input may also end up call functions within BadModule, but those don’t happen much - if at all - before the bug starts occurring, so those probably don’t matter.
Here are the things that the module does every frame while under the memory category:
debug.profilebegin(), debug.profileend(), and os.clock() are called a lot
Creating small temporary tables which will be cleaned up when we exit the scope
Sorting (usually small) arrays with table.sort() and a custom sorting function
For and while loops which do nothing because there is nothing to iterate over.
Writing to existing keys in the State table e.g. overwriting a number with another number.
Reads and occasionally writes data that resides outside of the module e.g. tables related to players. (Again mostly just overwriting existing keys with a different value of the same type.)
The module does a lot of other stuff, but the bug occurs before they have been done. I’ll mention them here anyway. These things do seem to affect the direction of the sawtooth pattern and the scale of the graph.
Creating potentially hundreds of thousands of tables, and does a lot of things with them including linked list operations, setting members, etc.
It creates Instances, parents them to the DataModel, and sets properties.