Introducing the Luau VM memory exploration tools

Hello Creators!

We have released a new Developer Console tool to help you debug and solve issues with memory usage and memory leaks both in client and on the server.

The main focus of the tool is to provide insights into memory used by the Luau VM, but it can also give hints on where unparented engine instances are being held by scripts.

This tool is only visible if you are a developer of the experience.

To use the new tool, open the Developer Console using the F9 key or the Developer Console button in the Settings and select LuauHeap from the drop-down list.

To explore the current memory allocation, create snapshots using the Create Snapshot button on the right.

When multiple snapshots are created, you can compare differences in memory use by clicking on a snapshot in the list on the left and clicking Compare on a different snapshot in the same list.

:warning: Please note: :warning:

Be careful when making snapshots on the Server. We recommend first trying it out in Studio and then testing it live in a private instance.

  • Taking a server snapshot in a live experience is possible, but it can take a considerable amount of time that will be seen as a high ping by the players.

  • During the preview we received reports of server snapshots crashing the server because of a timeout. We are working to improve the performance of the capture to avoid such a failure case.

There are tabs for the information that is gathered in a snapshot, so let’s go through them.

‘Object Tags’ view

This is a simple view showing how much memory is used by each Luau VM type.

Using the snapshot comparison function, this view can be used to quickly glance at what kind of objects are being used the most.

‘Memory Categories’ view

This view shows memory usage grouped by memory category.

A memory category is assigned to an object at allocation time, taken from the value assigned to the running Luau thread.

By default, the memory category name is the name of the script. Custom memory category names can be assigned using the debug.setmemorycategory function. Multiple scripts often share the same memory category slot as their number is limited.

For additional information, check out the Memory Category feature announcement and related ‘debug’ library functions.

‘Object Classes’ view

This view shows how many instances of each Roblox engine class are being stored in scripts.

Note that the Size field represents only the memory that Luau VM needs to represent the target object, it doesn’t include any memory that the Roblox engine itself uses.

For example, a ‘Player’ instance only needs 32 bytes of memory inside Luau, but links to an engine class that uses multiple kilobytes of memory and can contain hundreds of other instances inside. Similar to ‘Object Tags’ view, by using the snapshot compare feature, it can provide a quick way to find issues with how specific instances are being created.

‘Graph’ view

This is the most detailed and complex view into the Luau memory. It shows an aggregated memory use tree where each element represents an allocated object and tree structure is derived from the shortest path between object references.

All memory starts at Luau VM ‘registry’ - this is where all references from Roblox engine to Luau are stored, such as functions connected to signals or the task library, ModuleScript return tables, global functions, tables and classes.

The tree shows how objects are linked to each other inside the VM.

Here are some entries you might see:

  • ‘Module @Path.To.Module’ is the table returned by the specified ModuleScript.

  • ‘name:123 =Path.To.Module’ is a function inside the specified Script. Functions often don’t have an associated name in which case the ‘name’ part will be missing. Line 1 often refers to the global script function.

  • ‘upvalue’ is a reference from the function to a variable outside it. Check out Lua 5.1 Reference Manual or Scope | Documentation - Roblox Creator Hub for more information.

  • ‘env’ refers to the environment of a function. Most commonly, this is a table representing the global scope of a Script. Check out Lua 5.1 Reference Manual for more information.

  • ‘globals’ refers to the environment of a thread.

  • ‘[key]’ is for objects used as table keys.

  • ‘array’ represents the table array part.

  • ‘stack’ refers to the array where all function locals are stored. It is owned by a thread object.

  • ‘constants’ is an array that a function uses for constant value lookups.

Each element has a ‘Size’ and ‘Self’ columns.

  1. ‘Self’ means the memory used by the element itself, for example, an array of 1000 elements might use 16000 bytes just for the memory block where elements are stored
  2. ‘Size’ will also include the memory used by each element of the array (when it’s a complex object like a table).

Elements smaller than 2KB are replaced with a single ‘…’ node.

‘Unique References’ view

This view shows instances that are unparented from the DataModel and are only reachable from scripts. We attempt to display one or more paths that pin the instance object.

Keep in mind that in some cases it’s ok for the script to be the only owner of an instance, but if you see large amounts of instances that you did not expect, check out the paths which keep holding them.

Often, this is caused by signal connections not being disconnected.

Known Issues

There are some known issues and limitations, some of which we plan to address soon:

  • Parallel Luau VM heaps are not included in the memory size reports

  • If an instance is held by a Parallel Luau VM, it will not show up in the ‘Unique References’ view

  • Server snapshots can timeout a live RCC server with a large Luau heap

  • We want to provide more info about the root of an unparented instance

  • Entries smaller than 2KB are replaced with ‘…’, we want to make this limit configurable

Please try it out and tell us what you think!

220 Likes

This topic was automatically opened after 10 minutes.

What does proto refer to in the Object Tags view?

Also is the size in the Object Classes view accurate? Why are EnumItems so large? EDIT: Nevermind I’m a fool, ignore this part.

Also, is there a way to use this to locate RBXScriptConnections that have been orphaned? How would we go about doing that if so?

Thanks either way. This will make optimization work a lot more data-driven. Any chances of us getting this supported for plugins in the future? We’ve been trying to improve the performance of the Rojo plugin so it’d be helpful.

18 Likes

Entirely new territory to micro-optimize and annoy everyone working on the project w/ me! Amazing!

srs: Thank you so much for this! This seriously helps optimization, and it’s going to reveal so much behavior to me that I didn’t know before. Great feature

21 Likes

Thanks for making this, this should be a really useful tool for keeping an eye on optimization of scripts. Is it possible to see exactly what the signal connections are to better manage them?

8 Likes

This is awesome! having such an accurate tool for finding unparented instances is highly beneficial for everyone.
However, I can’t help but wonder looking at this UI - are there any plans to make the UI itself more readable? A color scheme with different colors for each category/datatype would make it a lot easier to read, as it’s a bit hard to do so for me currently; which applies to most of the console really. Class icons for instances would be a great help, too!

19 Likes

Is there a feature that sorts the elements by their Count, Memory usage, etc? I think this new feature will be useful in debugging memory leaks

11 Likes

‘proto’ refers to the object representing the function’s code and associated data.
Having a lot of memory taken by ‘proto’ can mean that there is simply too much code or static lookup tables in the experience.
In some cases where code itself is created on the fly (like sending ModuleScripts to the Client) it may increase over time.

What exactly is an orphaned script connection?
Just to clarify, functions referenced by connections are stored in ‘registry’ root by the engine.

While internal function result is a simple versioned table, making it public requires fixing the format.
It should be possible after a few months of getting experience with what it should contain.

11 Likes

Excellent job, well done :+1: becoming more like VS and that’s only a good thing

8 Likes

Agreed, I have hope that the entire developer console will be getting a new coat of paint in the near future. Unfortunately, like with everything else, that new coat might be too buggy for real world use until months or a year after launch.

8 Likes

Thank you, as a programmer, I designed it myself!

There is an internal project to revamp many of the Developer Console tabs (like the dreaded Memory) and add even more new tools, but I don’t have a timeline for that.

23 Likes

As an example, if I make a connection like this:

game.Players.PlayerAdded:Connect(function(player)
  print("wow a player joined cool")
end)

there isn’t a reference to it, so there’s no way to ever disconnect it. This is generally a bad thing, but there’s not really a way to track those down right now.

10 Likes

This is really super freaking cool. Developers have been lacking specific memory diagnostic tools for years. One thing that I really like is how specific you guys are when it comes to defining terms and learning to how to understand these tools. Documentation for the Developer Console has been lacking, and so hopefully this doesn’t follow in those footsteps.

I really look forward to using this tool. Thank you to everyone who contributed to this.

7 Likes

Omg!!! My evening is decided. Been waiting ages for tools for this.

11 Likes

What is defined as “global” under memory categories. Is that just Roblox scripts running in the background? Considering it makes up a large amount of memory used, I think it’s pretty important.

6 Likes

This is much needed. Thank you! Now yall should increase server memory :wink:

9 Likes

Cool to see more tools like this, but I dont know if Im understanding this correctly.

For instance, does THIS mean that there is a memory leak in my game that keeps references to a lot of players for no reason?

Its a lot higher for Players than anything else, and the memory size for my DataManager are higher than normal too.

And as others have pointed out, the whole dev console needs a redesign at this point. Ever since the additions of more tabs, its now collapsed into a dropdown by default, which isnt great.

EDIT: It IS a memory leak!

local function HandlePlayerConfig(player, playerConfig)
	player.CharacterAdded:Connect(function()
		-- stuff
	end)
end

I should have just looked at the Unique References tab lol, was able to find it instantly from there. Literal lifesaver.

10 Likes

Yes, this is one of the most likely reasons.
Keeping a connection attached to the player properties/events can also cause this.
References to players in a case like this should be reported in the ‘Unique References’ tab. If not, such a big chunk of memory should be easy to find in the ‘Graph’.

Consider testing out the new Player and Character auto-destroy behavior: New Player and Character Destroy Behavior
Or try to clean up the references manually if that doesn’t help (maybe you have a table where all players are recorded forever).

7 Likes

I will refer to Dan’s answer from yesterday on this:

6 Likes

Finally! I just literally Finished coding for today and saw this Announcement, This will help a Lot in making a lesser Memory footprint Instead of just reading a script over and over again… Especilly to complex/large Script’s, For my current case it helps a lot with Hardware Emulation and Terrain Generation. Thanks!

6 Likes