Summary
- Overall Lua heap memory helps track memory usage and the counts of important Lua object types, including tables, userdata, and functions.
- Heap allocation call stacks help identify Lua functions responsible for substantial allocations and guide targeted optimization.
- Reverse call-stack analysis makes it easier to trace allocations to the Lua function and source location responsible for them.
- Cached C# object references can help explain why objects remain referenced and cannot be released as expected.
- Lua source files provide more useful function-location information than bytecode for this analysis, because the reported ending line number is always
0when Lua scripts run as bytecode.
What Are the Main Lua Memory Problems in Unity?
Lua is commonly used to support hot updates, allowing development teams to add features and fix bugs without rebuilding the entire game. However, as Lua usage grows, memory allocation and object lifetime management can introduce runtime performance problems.
Two issues deserve particular attention:
- Excessive memory allocation: Lua functions allocate substantial amounts of heap memory, potentially increasing garbage collection (GC) frequency and the cost of GC operations.
- Unreleased references: Lua-related references to C# objects remain in a cache, preventing those objects from being released as expected and potentially contributing to C# memory leaks.
These problems require different investigations. Allocation analysis helps identify where memory is being created, while reference analysis helps identify objects that remain referenced over time.
How Can You Analyze Overall Lua Heap Memory?
The overall heap memory report tracks Lua's total memory usage over time, along with the counts of three important Lua object types:
- Table
- Userdata
- Function
By examining these metrics at different frames, developers can determine how Lua memory usage evolves during gameplay.
What Should You Look for in the Memory Trend?
Start by examining Lua's total heap memory across the captured session.
Ask the following questions:
- Does Lua memory remain within the expected range during the tested gameplay?
- Does memory usage continue to increase as the game runs?
- Do changes in total memory coincide with changes in the number of tables, userdata objects, or functions?
The purpose is to establish whether the observed memory usage matches expectations and whether the trend warrants further investigation.
An increasing memory trend is a reason to investigate, not sufficient proof of a memory leak. Use the overall report to identify suspicious periods, then inspect detailed allocations or object references to understand what is happening.
How Can You Find the Lua Functions Allocating the Most Memory?
The overall heap report shows the memory trend, but it does not by itself identify the functions responsible for allocations.
The detailed heap allocation report addresses this problem by allowing developers to inspect allocation call stacks for a specific frame.
This helps answer two practical questions:
- Which Lua functions are responsible for substantial heap allocations?
- Which lines of Lua code should be investigated for optimization?
Step 1: Select a Frame and Inspect Allocation Call Stacks
Choose a frame of interest and examine the allocation call stacks recorded for that frame.
Focus on functions associated with substantial allocations. Their call stacks provide the context needed to trace allocations through the Lua code.
This approach helps narrow the investigation from a game-wide memory problem to specific functions that may be worth optimizing.
Step 2: Use Reverse Call-Stack Analysis to Locate the Source
The report also supports a reverse call-stack viewing mode, similar to the reverse analysis available for Mono heap memory.
Switching the viewing mode makes it easier to identify the deeper Lua functions responsible for allocations, rather than having to work through the original call-stack order.
The goal is to locate the specific Lua script and source line associated with substantial heap allocation. Developers can then inspect the corresponding code and determine whether the allocation behavior can be improved.
Why Does Lua Function Naming Matter?
Lua functions are represented using the following naming format:
X@Y:Z
| Component | Meaning |
|---|---|
X |
Function name. If the name cannot be obtained, it is shown as unknown. |
Y |
Location of the file where the function is defined. |
Z |
Ending line number of the function definition. |
There is an important limitation: when Lua scripts run as bytecode, the reported ending line number is always 0.
For this reason, developers should use Lua source files during testing whenever possible. Source-based testing provides more useful location information when tracing allocations back to the code that produced them.
How Can Lua References Cause C# Memory Leaks?
Lua memory analysis is not limited to allocations within the Lua heap. References between Lua and C# can also affect object lifetimes in Unity projects.
Lua plugins commonly maintain a cache on the C# side for objects accessed by Lua. This cache helps prevent a C# object from being garbage-collected while Lua still needs to access it.
Without an appropriate reference-management mechanism, a C# object could be collected before Lua attempts to access it again, potentially causing logic errors.
However, a cached reference can also prevent an object from being released when it is no longer needed. If unnecessary references accumulate, they can contribute to memory leaks on the C# side.
This is why investigating Lua-related memory issues sometimes requires examining Mono objects rather than looking only at Lua heap usage.
How Does the Mono Object Reference Report Help?
The Mono object reference report summarizes C# objects held in the relevant caches maintained by Lua plugins.
It provides two useful types of information:
- Object types and counts: the types of C# objects present in the cache and the total number of objects for each type.
- Destroyed Unity objects: for types inheriting from
UnityEngine.Object, the report also counts objects that have already been destroyed.
The second metric is particularly useful when investigating references to Unity objects that have been destroyed but may still appear in the cache.
How Can You Identify Suspicious Object References?
Follow this investigation sequence:
- Open the Mono object reference report and review the object types and their counts.
- Identify object types whose counts continue to increase across sampling points.
- Compare the references at two different sampling points for a suspicious object type.
- Use the differences to investigate which Lua references may be unnecessary or improperly retained.
The report helps narrow the investigation to specific object types and changes in their reference counts. Developers can then inspect the relevant Lua code and reference-management logic.
A growing cache count does not automatically establish that every retained object is leaked. The key is to determine whether those references are still required by the game's logic.
What Should You Check First When Investigating Lua Memory Issues?
The most efficient starting point depends on the symptom you observe.
| Observed symptom | What to inspect first | Next step |
|---|---|---|
| Lua heap memory is unexpectedly high | Overall heap memory trend and Lua object counts | Identify suspicious periods and inspect detailed allocations |
| Lua heap allocations are substantial | Per-frame allocation call stacks | Use reverse call-stack analysis to locate the relevant Lua functions |
| C# object counts continue to increase | Mono object reference report | Compare object references at different sampling points |
| Destroyed Unity objects remain in the cache | Destroyed-object counts for types inheriting from UnityEngine.Object
|
Investigate whether Lua-related references are still being retained |
| Allocation locations are difficult to identify | Lua function names and source-location information | Prefer source-based Lua testing over bytecode when possible |
This sequence separates three distinct questions: how much memory Lua uses, where Lua allocates memory, and which C# objects remain referenced.
Investigating the appropriate report first helps avoid optimizing unrelated code or assuming that every memory increase has the same cause.
Which Lua Memory Analysis Tools Are Supported?
GameOptim GOT Online's Lua memory analysis supports several widely used Lua plugins:
- uLua
- ToLua
- SLua
- xLua
If a development team recompiles the Lua native library under a custom name, the library name can be specified through the UWAEngine.SetOverrideLuaLib interface.
This configuration is relevant when the project's Lua native library does not use one of the expected library names. Correctly identifying the library helps the analysis support the project's Lua integration.
Key Takeaways
- Track trends before diagnosing leaks. Overall Lua heap memory and object counts help identify suspicious changes during gameplay.
- Trace allocations to their source. Per-frame allocation call stacks and reverse stack viewing help locate Lua functions and source lines responsible for substantial allocations.
- Use Lua source files during testing when possible. Bytecode execution causes the reported function-ending line number to remain
0. - Investigate references as well as allocations. Lua plugin caches can retain C# objects and contribute to memory leaks if references are no longer needed.
- Compare multiple sampling points. Changes in cached object counts and references help narrow down suspicious object lifetimes.
FAQ
1. Does increasing Lua heap memory always indicate a memory leak?
No. An increase in Lua heap memory is a signal to investigate, but it does not prove that a memory leak exists. Check the memory trend, object counts, and detailed allocation data before drawing a conclusion.
2. How can I find the Lua function responsible for excessive allocations?
Use the detailed heap allocation report to inspect allocation call stacks for a specific frame. Reverse call-stack viewing can help trace substantial allocations to the relevant Lua function and source location.
3. Why does the Lua function's ending line number show 0?
When Lua scripts run as bytecode, the reported ending line number is always 0. For more useful source-location information during allocation analysis, use Lua source files in testing whenever possible.
4. Why can Lua cause memory leaks in C# objects?
Lua plugins commonly maintain C#-side caches to keep objects referenced by Lua from being collected prematurely. If objects remain in these caches after the references are no longer needed, they may not be released as expected, contributing to C# memory leaks.
5. How can I investigate destroyed Unity objects that remain referenced?
Use the Mono object reference report to inspect destroyed-object counts for cached types that inherit from UnityEngine.Object. Compare references across sampling points and investigate whether Lua-related references are being retained unnecessarily.
Top comments (0)