UE5 记录DataRegistry"使用不当"导致的卡死问题
根本原因尝试释放一个已经释放过的内存地址(重复释放内存, 未定义行为)
DataRegistry自带一个缓存机制(详见FCachedDataRegistryItem),设计上是想要引擎来管理内存,
我当时虽然看到了,但没重视这个设计点,在拓展的时候(建了个自定义的UDataRegistrySource),想自己管理这块内存,
所以游戏结束的时候(退出PIE),我就自己在对应的回调里释放了这个内存
之后,引擎当然认为它自己管理着内存,也去释放它,
于是就寄了
表现
结果看起来很简单,但一开始的表现却很诡异.误导了我的排查方向
直接表现是退出PIE的时候,编辑器直接卡死,没任何反应也没闪退.
原因探查
mimalloc
使用调试的暂停按钮,发现游戏线程卡在一个"奇怪"的第三方内存分配器库---mimalloc,的代码里:
// part of call stack
_mi_page_free_collect
mi_heap_page_collect
mi_heap_visit_pages(heap, &mi_heap_page_collect, &collect, NULL);
mi_collect(bTrimThreadCaches);
GMalloc->Trim(bTrimThreadCaches);
FMemory::Trim()
CollectGarbageInternal(KeepFlags, bPerformFullPurge);
CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS);
UEditorEngine::EndPlayMap
_mi_page_free_collect - page.c
mimalloc是UE5默认的内存分配器,因为性能和内存占用相对都很不错:
FWindowsPlatformMemory::BaseAllocator
在跟了一会这个现场代码之后,看它只是在无限的循环中尝试释放下一个page的内存,没任何有用的信息.
并且手动的展开这个链表(几十次后),发现它是有结束的节点(next为nullptr),但这个循环却没有停下,
要么是block在了途中,要么循环中途next指针一直在变化
引擎问题?
因为本地用的是5.1的分支,并且开了好多插件,我首先怀疑了是不稳定的代码导致的,
首先排除插件问题,本想二分禁用插件看看,但数量过多,于是直接在.uproject把显式启用的插件全删了,
这样只有必要的插件会自动启用,
结果还是会卡死,于是排除了插件问题
然后用引擎打开lyra项目,发现一切正常,引擎问题也排除了
是自己拓展的UDataRegistrySource的代码问题?
换一个地图,因为没使用到相关的自定义代码,正常退出PIE!
于是石锤是自己的锅...(虽然引擎问题不少,但80%的情况下它都是躺枪)
问题的解决
想到最近好像看过一篇,关于类似卡死问题的通用解法的 @quabqi 大佬文章(感谢!):
于是使用了文中提到的Stomp内存分配器,虽然开启之后肉眼可见的性能下降(目测下降5-10倍,类似引擎编译配置的development VS debug),但感觉确实是内存问题的排查利器,药到病除的感觉哈哈(先吹一波).
使用方法是将上图FWindowsPlatformMemory::BaseAllocator中的枚举改成Stomp就好了,
它会在全局内存分配器的创建函数FMemory_GCreateMalloc_ThreadUnsafe中被调用
于是终于发现了"内存损坏"(UB)的现场堆栈:
FMallocStomp::Free
FMEMORY_INLINE_GMalloc->Free(Original);
FMemory::Free(ItemMemory);
FCachedDataRegistryItem::FreeItemMemory // UDataRegistry尝试释放内存
FCachedDataRegistryItem::ClearItemMemory
~FCachedDataRegistryItem
FDataRegistryCache::ClearCache
UDataRegistrySource::ResetRuntimeState // 我在这里先释放掉了内存
UDataRegistry::ResetRuntimeState
UDataRegistrySubsystem::ResetRuntimeState
UDataRegistrySubsystem::EndPIE
下次在遇到类似的问题,感觉可以先直接开stomp看看,避免走弯路~ 大佬 感谢支持[爱]
页:
[1]