0
UnityFramework
FixedSizeMemoryPool_Free_mB4629DBAF35C7A2A4A20C0F358856866B7F1B403(Unity.ThirdParty.cpp:0) [inlined: Slab_Free_m625CA9D00C1C1A09733BEAA7CD85A11C1AE872CC_inline(Slab_t61115C02AC26DE325FAF380759703CB08EDE384E*, ListNode_tCD74AA7B74A7548CEB7214E822BDF23BEA6485FC*, MethodInfo const*)(Downloads/iosBuild(12)/Il2CppOutputProject/Source/il2cppOutput/Unity.ThirdParty.cpp:27121)
1
UnityFramework
SortedSet_1_DoRemove_m85F4E0F134DA5F7D0C44B040890C3A4E4E13A2AC_gshared(Generics__130.cpp:0)
2
UnityFramework
SortedSet_1_DoRemove_m85F4E0F134DA5F7D0C44B040890C3A4E4E13A2AC_gshared(Generics__130.cpp:0)
3
UnityFramework
MultiMap_2_Remove_m71969908BD51D8A0158D8370939D1FA0A347D9CE_gshared_inline(MultiMap_2_tED68F769D3AAD03FE12809062A77E5CF40C04826*, long long*, MethodInfo const*)(Generics103.cpp:0) [inlined: SortedSet_1_DoRemove_m85F4E0F134DA5F7D0C44B040890C3A4E4E13A2AC(SortedSet_1_tBF36007EC303BFCDB68F4997A74B697D00748ADF*, MultiMapPair_2_t1DC6EC51DD9DAB7BB84C668CD6B0243EF47E66A6*, MethodInfo const*)(Downloads/iosBuild(12)/Il2CppOutputProject/Source/il2cppOutput/Generics103.cpp:6287)
4
UnityFramework
TimerComponentSystem_Update_m646E8959E443BBC61FF0F913F4C26C49D01ED1E1(Unity.Core.cpp:16931)
5
UnityFramework
EntitySystem_Update_mED9A90063D93A745F51BBD3EC53E57F6B820B67B(Unity.Core.cpp:11608)
6
UnityFramework
Fiber_Update_m86DBB997FFB96E1449E6FF4EBB15065C74ECAA64(Unity.Core.cpp:12071)
7
UnityFramework
MainThreadScheduler_Update_m188313AEE15DB7F3793F6F6620A6F3612BE9FE51(Unity.Core__1.cpp:16955)
8
UnityFramework
il2cpp::vm::Runtime::InvokeWithThrow(MethodInfo const*, void*, void**)(Runtime.cpp:0)
9
UnityFramework
il2cpp::vm::Runtime::Invoke(MethodInfo const*, void*, void**, Il2CppException**)(Runtime.cpp:606)
10
UnityFramework
scripting_method_invoke(ScriptingMethodPtr, ScriptingObjectPtr, ScriptingArguments&, ScriptingExceptionPtr*, bool)(ScriptingApi_Il2Cpp.cpp:290)
11
UnityFramework
ScriptingInvocation::Invoke(ScriptingExceptionPtr*, bool)(ScriptingInvocation.cpp:298)
12
UnityFramework
MonoBehaviour::CallUpdateMethod(int)(MonoBehaviour.cpp:593)
13
UnityFramework
CommonUpdate<BehaviourManager>(Behaviour.cpp:158) [inlined: UpdateBehaviour(Behaviour.cpp:180)
14
UnityFramework
ExecutePlayerLoop(NativePlayerLoopSystem*)(PlayerLoop.cpp:393)
15
UnityFramework
ExecutePlayerLoop(NativePlayerLoopSystem*)(PlayerLoop.cpp:417)
16
UnityFramework
PlayerLoop()(PlayerLoop.cpp:527)
17
UnityFramework
UnityPlayerLoopImpl(bool)(LibEntryPoint.mm:342)
18
UnityFramework
UnityRepaint(UnityAppController+Rendering.mm:218)
19
QuartzCore
CA::Display::DisplayLinkItem::dispatch_(CA::SignPost::Interval<(CA::SignPost::CAEventCode)835322056>&) + 48
AI给的分析:
嗯,用户遇到了一个SIGSEGV错误,具体是SEGV_ACCERR,这通常意味着内存访问权限问题。调用栈显示问题出在memmove函数,而源头是TimerComponentSystem的Update方法中的MultiMap操作。首先,我需要分析调用栈和相关代码,找出可能的原因。
首先看调用栈,崩溃发生在Stack_1_Push,可能是在操作MultiMap时发生了内存错误。用户提供的代码中,TimerComponent使用了NativeCollection.MultiMap,而MultiMap内部可能涉及非托管内存操作。SEGV_ACCERR可能是因为访问了已释放的内存,或者多线程竞争导致的数据不一致。
接下来看TimerComponentSystem的Update方法。在遍历timeId时,如果另一个线程或主线程的其他操作修改了timeId集合,可能会导致迭代过程中出现并发修改,进而引发内存错误。例如,在foreach循环中遍历timeId时,如果有其他代码同时修改了timeId,可能导致内部结构损坏,从而在memmove时崩溃。
另外,MultiMap的Add和Remove方法可能没有正确的线程安全措施。如果TimerComponent被多个线程访问,而MultiMap不是线程安全的,就会导致竞态条件。例如,在AddTimer方法中,向timeId添加条目时,如果同时有另一个线程在修改同一个结构,可能破坏内存。
还要检查MultiMap的实现,特别是UnsafeType.MultiMap的Dispose方法。如果在某个时刻MultiMap被释放了,但TimerComponent还在使用它,就会访问野指针,导致SEGV_ACCERR。需要确认MultiMap的生命周期是否正确管理,没有提前释放的情况。
此外,代码中的timeOutTime和timeOutTimerIds是Queue结构,如果在多线程环境下被并发访问,也可能导致问题。但根据ET框架的设计,可能这些操作都在主线程执行,所以需要确认是否存在异步操作修改了这些集合。
可能的解决方案包括:确保所有对MultiMap和Queue的访问都在同一线程;在遍历时使用锁或其他同步机制;检查MultiMap的Dispose是否被错误调用;以及使用线程安全的集合类型。此外,使用调试工具如Address Sanitizer或分析崩溃现场的变量状态,可以帮助定位具体的内存问题。
摘要/原因
问题名称:多线程环境下非托管内存操作导致的非法内存访问
关键特征:
异常类型 SIGSEGV/SEGV_ACCERR
表示尝试访问未分配或已释放的内存区域
调用栈显示崩溃发生在 Stack_1_Push
操作,对应 MultiMap.Add
方法
相关代码涉及 NativeCollection.MultiMap
的非托管内存操作
可能原因
1. 多线程竞争导致内存损坏(高概率)
- 分析依据:
调用栈显示崩溃发生在 TimerComponentSystem.Update
方法,该方法正在遍历 timeId
集合。
若存在其他线程(如 ECS 的 JobSystem)同时修改 MultiMap
或 Dictionary
,会导致内存结构损坏。
特别是 NativeCollection.MultiMap
使用非托管内存,缺乏自动线程安全机制。
2. 已释放内存的访问(中概率)
- 分析依据:
MultiMap
实现了 INativeCollectionClass
接口,若在 TimerComponent
使用过程中被意外 Dispose()
,后续操作将访问野指针。
相关代码中未看到明确的 Dispose
保护逻辑。
3. 集合迭代过程中修改(中概率)
- 分析依据:
Update
方法使用 foreach (var kv in self.timeId)
遍历集合,
若在遍历过程中其他逻辑(如 AddTimer
/Remove
)修改了 timeId
集合,会导致迭代器失效。
解决方案
1. 添加线程同步机制
$$
// 修改 TimerComponent 定义 public class TimerComponent: Entity, IAwake, IUpdate { // 添加读写锁 private readonly ReaderWriterLockSlim _lock = new(); public void AddTimerWithLock(long timerId, ref TimerAction timer) { _lock.EnterWriteLock(); try { AddTimer(timerId, ref timer); } finally { lock.ExitWriteLock(); } } } // 修改 Update 方法 [EntitySystem] private static void Update(this TimerComponent self) { self.lock.EnterReadLock(); try { // 原有遍历逻辑 foreach (var kv in self.timeId) { … } } finally { self._lock.ExitReadLock(); } }
$$
2. 确保集合迭代安全
$$
// 修改遍历逻辑为副本遍历 foreach (var kv in self.timeId.ToArray()) // 创建副本 { if (k > timeNow) {…} self.timeOutTime.Enqueue(k); // 注意这里仍需同步 }
$$
3. 检查 NativeCollection 生命周期
$$
// 在 TimerComponent 中增加保护 public override void Dispose() { if (IsDisposed) return; _lock.EnterWriteLock(); try { timeId.Dispose(); base.Dispose(); } finally { _lock.ExitWriteLock(); } }
$$
后续分析提示
内存分析:
- 使用 Xcode 的 Address Sanitizer 检查内存操作
- 在
MultiMap.Add
前后添加日志输出指针地址
线程验证:
$$
// 在关键方法添加线程检查 UnityEngine.Debug.Assert(UnityEngine.Object.IsMainThread(), “TimerComponent accessed from non-main thread!”);
$$
崩溃现场验证:
建议提供信息:
完整的 UnsafeType.MultiMap
实现代码
崩溃时的 TimeInfo.Instance.ServerFrameTime()
数值
是否启用了 ECS 的 JobSystem 或多线程渲染
建议优先添加线程断言和同步机制,使用 Xcode 的 Thread Sanitizer 进行验证。如果问题仍然出现,需要检查 NativeCollection 的内存分配对齐是否符合 iOS 的 ARM64 架构要求(16字节对齐)。