[Unity] Bug: js GC not called when after print object
前置阅读 | Pre-reading
Puer的版本 | Puer Version
v2.0.0-pre.4,ApiLevel:31
Unity的版本 | Unity Version
2021.3.17f
发生在哪个平台 | Platform
win
错误信息 | Error Message
No response
问题重现 | Bug reproduce
您好, Hello,
我在使用Unity的Puerts通过JavaScript创建C#类的新实例时遇到了一个问题,即内存似乎没有正确释放。我在C#中定义了一个名为TestClass的类,并在JavaScript中按照以下方式创建新实例:
I'm having an issue where memory does not seem to be properly freed when creating new instances of a C# class through Puerts in Unity. I have a class TestClass defined in C#, and I'm creating new instances of it in JavaScript as follows:
const TestClass = CS.TestClass;
var a = new TestClass();
a = new TestClass();
a = new TestClass();
a = new TestClass();
当我运行上述代码并通过Unity的内存分析器检查内存使用情况时,只有一个TestClass的实例似乎留在了内存中。 When I run the above code and check the memory usage via Unity's memory profiler, only one instance of TestClass appears to remain in memory. 但是,当我在每次新分配前添加一个console.log(a)语句时: However, when I add a console.log(a) statement before each new assignment like so:
const TestClass = CS.TestClass;
var a = new TestClass();
console.log(a);
a = new TestClass();
console.log(a);
a = new TestClass();
console.log(a);
a = new TestClass();
我看到四个TestClass的实例留在了内存中。 I see four instances of TestClass remaining in memory. 我还使用了PLog函数来检查是否调用了CDataGarbageCollectedWithFree和UnBindCppObject,它们在这些情况下似乎根本没有被调用。 I also used the PLog function to check if CDataGarbageCollectedWithFree and UnBindCppObject were called. They do not appear to be called at all in these cases. 我不确定我是否做错了什么,或者这是否是Puerts的一个bug。我将感谢任何关于此问题的指导或可能的修复方法。 I'm not sure if I'm doing something wrong or if this is a bug in Puerts. I would appreciate any guidance or potential fixes for this issue.
请注意,TestClass是一个空的C#类,并且测试是在IL2CPP Windows构建上进行的。
Please note that TestClass is an empty C# class, and the testing was performed on an IL2CPP Windows build.
谢谢
Thank you
这个是console.log的特性,打印一个object,估计为了避免每次都toString,内部做了cache。 如果要释放试试手动调用console.clear()
还有一个避免的方式是不要直接用对象做参数,可以先手动toString 也就是别写consolo.log(obj) 写console.log(obj.toString()
谢谢! Thanks a lot @chexiongsheng !! 大多数时候,当我运行上述代码并通过Unity的内存分析器检查内存使用情况时,只有一个TestClass的实例似乎留在了内存中。然而,偶尔在内存分析器中观察到两个TestClass的实例。 Most of the time when I run the above code and check the memory usage via Unity's memory profiler, only one instance of TestClass appears to remain in memory. However, on occasion, two instances of TestClass are observed in the memory profiler. 每个 js 代码都是单独评估的。 Each js code is evaluated saperately.
JsEnv env = new JsEnv();
env.Eval(@"
const TestClass = CS.TestClass;
var a = new TestClass();");
env.Eval(@"a = new TestClass();");
env.Eval(@"a = new TestClass();");
env.Eval(@"a = new TestClass();");
在这种情况下,我没有使用console.log。即使我像上面那样只是分配一个新的TestClass(),有时候内存似乎也没有得到适当的释放。 I'm not using console.log in this instance. It seems like sometimes the memory is not properly freed even if I'm just assigning a new TestClass() like above.
谢谢! Thanks a lot @chexiongsheng !! 大多数时候,当我运行上述代码并通过Unity的内存分析器检查内存使用情况时,只有一个TestClass的实例似乎留在了内存中。然而,偶尔在内存分析器中观察到两个TestClass的实例。 Most of the time when I run the above code and check the memory usage via Unity's memory profiler, only one instance of TestClass appears to remain in memory. However, on occasion, two instances of TestClass are observed in the memory profiler.
const TestClass = CS.TestClass; var a = new TestClass(); a = new TestClass(); a = new TestClass(); a = new TestClass();在这种情况下,我没有使用console.log。即使我像上面那样只是分配一个新的TestClass(),有时候内存似乎也没有得到适当的释放。 I'm not using console.log in this instance. It seems like sometimes the memory is not properly freed even if I'm just assigning a new TestClass() like above.
完整做完一次gc才能保证无引用对象释放。 但由于一次完整的gc开销很大,一个gc都会拆分成多个阶段、步骤,按其策略根据当前应用的内存状况来执行,所以已经没引用的对象仍然在内存一段时间是正常现象。
@chexiongsheng 感谢您之前的回复。我明白要保证无引用对象的释放,需要进行一次完整的垃圾回收(GC)周期,由于一次完整的GC开销很大,所以它会被拆分为多个阶段,并根据当前应用的内存状况来执行。这意味着无引用的对象在内存中保留一段时间是正常的。
然而,我注意到了一个进一步的问题,即即使在调用垃圾回收器(GC)多次和经过了大量的时间后,实例的最小计数并不会减少。
以下是我用TestClass观察到的事件顺序:
我将TestClass分配给了a五次,这导致实例计数为5(根据Unity的内存分析器)。 调用GC后,实例计数减少到1。 TestClass被分配给a又三次,使实例计数达到4。 再次调用GC,实例计数减少到2。 这种模式持续,每次TestClass被分配和每次GC被调用时,实例计数都会波动。然而,即使在等待超过一个小时的时间(期间TestClass可能被分配很多次,并且频繁地调用GC)后,实例的最小计数(即GC调用后的实例计数)只会增加或保持不变。
我发现,一旦最小实例计数增加,它就不会减少 - 它只会增加或保持不变。请问这种行为是否符合垃圾收集器的正常运作预期,或者这可能表明存在内存管理问题?
谢谢您的持续协助。 Thank you for your previous response. I understand that a complete garbage collection (GC) cycle is needed to guarantee the release of unreferenced objects, and due to the high overhead of a full GC, it is split into multiple phases and is executed according to the current memory situation of the application. This means that it's normal for unreferenced objects to remain in memory for a while.
However, I've noticed a further issue where the minimum count of instances does not decrease, even after multiple invocations of the garbage collector (GC) and a significant elapsed time.
The following describes the observed sequence of events with my TestClass:
I assign TestClass to a five times, resulting in an instance count of 5 (as per Unity's memory profiler). The GC is invoked, reducing the instance count to 1. TestClass is assigned to a three more times, bringing the instance count to 4. The GC is invoked again, lowering the instance count to 2. This pattern continues, with the instance count fluctuating each time TestClass is assigned and each time the GC is invoked. However, the minimum instance count (that is, the count of instances following a GC invocation) only ever increases or stays the same, even when waiting for over an hour, during which time TestClass may be assigned many times and the GC invoked frequently.
I'm finding that once the minimum instance count increases, it never decreases - it only ever increases or remains the same. Is this behavior within the normal expectations of the garbage collector's operation, or could this be indicative of a memory management issue?
Thank you for your continued assistance.
你可以手动调用下RequestFullGarbageCollectionForTesting 确保一次完整的gc,然后观测对象的释放情况。
@chexiongsheng 感谢您之前的回复。我明白要保证无引用对象的释放,需要进行一次完整的垃圾回收(GC)周期,由于一次完整的GC开销很大,所以它会被拆分为多个阶段,并根据当前应用的内存状况来执行。这意味着无引用的对象在内存中保留一段时间是正常的。
然而,我注意到了一个进一步的问题,即即使在调用垃圾回收器(GC)多次和经过了大量的时间后,实例的最小计数并不会减少。
以下是我用TestClass观察到的事件顺序:
我将TestClass分配给了a五次,这导致实例计数为5(根据Unity的内存分析器)。 调用GC后,实例计数减少到1。 TestClass被分配给a又三次,使实例计数达到4。 再次调用GC,实例计数减少到2。 这种模式持续,每次TestClass被分配和每次GC被调用时,实例计数都会波动。然而,即使在等待超过一个小时的时间(期间TestClass可能被分配很多次,并且频繁地调用GC)后,实例的最小计数(即GC调用后的实例计数)只会增加或保持不变。
我发现,一旦最小实例计数增加,它就不会减少 - 它只会增加或保持不变。请问这种行为是否符合垃圾收集器的正常运作预期,或者这可能表明存在内存管理问题?
谢谢您的持续协助。 Thank you for your previous response. I understand that a complete garbage collection (GC) cycle is needed to guarantee the release of unreferenced objects, and due to the high overhead of a full GC, it is split into multiple phases and is executed according to the current memory situation of the application. This means that it's normal for unreferenced objects to remain in memory for a while.
However, I've noticed a further issue where the minimum count of instances does not decrease, even after multiple invocations of the garbage collector (GC) and a significant elapsed time.
The following describes the observed sequence of events with my TestClass:
I assign TestClass to a five times, resulting in an instance count of 5 (as per Unity's memory profiler). The GC is invoked, reducing the instance count to 1. TestClass is assigned to a three more times, bringing the instance count to 4. The GC is invoked again, lowering the instance count to 2. This pattern continues, with the instance count fluctuating each time TestClass is assigned and each time the GC is invoked. However, the minimum instance count (that is, the count of instances following a GC invocation) only ever increases or stays the same, even when waiting for over an hour, during which time TestClass may be assigned many times and the GC invoked frequently.
I'm finding that once the minimum instance count increases, it never decreases - it only ever increases or remains the same. Is this behavior within the normal expectations of the garbage collector's operation, or could this be indicative of a memory management issue?
Thank you for your continued assistance.
You can use chrome devtool to make a Javascript side's heapdump and you may get more information, because those TestClass in C# side is kept by Javascript by PuerTS.
you can follow these two tutorial, the latter is in Chinese only so you may use translation tool to translate. Or you can find me in discord to ask me any question.
https://puerts.github.io/en/docs/puerts/unity/knowjs/debugging https://zhuanlan.zhihu.com/p/359598262