focus-creative-games / hybridclr

HybridCLR是一个特性完整、零成本、高性能、低内存的Unity全平台原生c#热更新解决方案。 HybridCLR is a fully featured, zero-cost, high-performance, low-memory solution for Unity's all-platform native c# hotupdate.
https://code-philosophy.com/
MIT License
6.51k stars 656 forks source link

MachineState 内存泄漏问题 #16

Closed AlanLiu90 closed 2 years ago

AlanLiu90 commented 2 years ago

看代码的时候,发现 MachineState 没有地方释放,并且析构函数中 _frameBase 和 _exceptionFlowBase 也没有释放。 用 Unity 2020.3.33+Win64,运行下面的代码,内存会不断增长:

void Start()
{
    StartCoroutine(Test());
}

IEnumerator Test()
{
    Thread[] threads = new Thread[4];

    while (true)
    {
        for (int i = 0; i < threads.Length; ++i)
        {
            Thread t = new Thread(Run);
            threads[i] = t;
            t.Start();
        }

        for (int i = 0; i < threads.Length; ++i)
        {
            threads[i].Join();
            threads[i] = null;
        }

        yield return new WaitForSeconds(5);
    }
}

void Run()
{
    Thread.Sleep(1);
}

我自己尝试改了下代码验证,发现确实是 MachineState 没释放导致的:

--- a/hybridclr/interpreter/Engine.h
+++ b/hybridclr/interpreter/Engine.h
@@ -56,6 +56,8 @@ namespace interpreter

        ~MachineState()
        {
+           IL2CPP_FREE(_exceptionFlowBase);
+           IL2CPP_FREE(_frameBase);
            il2cpp::gc::GarbageCollector::FreeFixed(_stackBase);
        }

--- a/hybridclr/interpreter/InterpreterModule.h
+++ b/hybridclr/interpreter/InterpreterModule.h
@@ -19,6 +19,7 @@ namespace interpreter

        static MachineState& GetCurrentThreadMachineState();
+       static void ClearCurrentThreadMachineState();

        static InterpMethodInfo* GetInterpMethodInfo(const MethodInfo* methodInfo);

--- a/hybridclr/interpreter/InterpreterModule.cpp
+++ b/hybridclr/interpreter/InterpreterModule.cpp
@@ -38,6 +38,18 @@ namespace hybridclr
            return *state;
        }

+       void InterpreterModule::ClearCurrentThreadMachineState()
+       {
+           MachineState* state = nullptr;
+           s_machineState.GetValue((void**)&state);
+
+           if (state)
+           {
+               s_machineState.SetValue(nullptr);
+               delete state;
+           }
+       }
+
        void InterpreterModule::Initialize()
        {
            for (size_t i = 0; ; i++)
--- a/libil2cpp/vm/Thread.cpp
+++ b/libil2cpp/vm/Thread.cpp
@@ -29,6 +29,8 @@
 #include "Cpp/Atomic.h"
 #include "Cpp/ReentrantLock.h"

+#include "hybridclr/interpreter/InterpreterModule.h"
+
 #if IL2CPP_MONO_DEBUGGER

 extern "C" {
@@ -355,6 +357,8 @@ namespace vm

         Uninitialize(thread);
         il2cpp::vm::StackTrace::CleanupStackTracesForCurrentThread();
+
+        hybridclr::interpreter::InterpreterModule::ClearCurrentThreadMachineState();
     }

     Il2CppThread* Thread::Main()
@@ -730,6 +734,8 @@ namespace vm
                 il2cpp::vm::Thread::Uninitialize(startData->m_Thread);

             il2cpp::vm::StackTrace::CleanupStackTracesForCurrentThread();
+
+            hybridclr::interpreter::InterpreterModule::ClearCurrentThreadMachineState();
         }

         delete startData->m_Semaphore;

--- a/libil2cpp/icalls/mscorlib/System.Threading/Thread.cpp
+++ b/libil2cpp/icalls/mscorlib/System.Threading/Thread.cpp
@@ -20,6 +20,8 @@
 #include "utils/Memory.h"
 #include "utils/StringUtils.h"

+#include "hybridclr/interpreter/InterpreterModule.h"
+
 using il2cpp::gc::GarbageCollector;

 namespace il2cpp
@@ -169,6 +171,8 @@ namespace Threading
             il2cpp::vm::Thread::Uninitialize(startData->m_Thread);

             il2cpp::vm::StackTrace::CleanupStackTracesForCurrentThread();
+
+            hybridclr::interpreter::InterpreterModule::ClearCurrentThreadMachineState();
         }

         delete startData->m_Semaphore;
pirunxi commented 2 years ago

感谢。这个问题是已知的,因为觉得客户端没人会不停地创建线程。我们考虑某个时候修复它。

AlanLiu90 commented 2 years ago

我们游戏刚好是会受这个问题影响的:游戏是区分大厅和战斗场景的,每次开启新的一局游戏(进入战斗场景),导航系统(不是Unity的,是我们自己实现的)就会创建几个线程,离开战斗场景时,这些线程会随着导航系统销毁而销毁。

官方修复这个问题前,我们先自己解决。

pirunxi commented 2 years ago

我们现在就修复它

pirunxi commented 2 years ago

这个问题已经修复提交