IOS
机制:主程序被load时,全局符号的地址 会替换 之前加载的动态库的 同名符号的地址。
内存池:使用重载operator new/delete 的 MallocBinned。
现状:
Dev版本:__Znwm、__ZdlPv 等重载的内存操作函数,会替换如libc++.1.dylib的 __Znwm、__ZdlPv 的地址,使得 libc++.1.dylib 的内存分配释放(包括std::string) 使用的是UE重载的operator new/delete。
Shipping版本: 由于在链接时比Dev版本多了一步 strip,这个将 __Znwm、__ZdlPv 符号去除了。然后在启动时,__Znwm、__ZdlPv 的地址不会再替换 其它动态库的,所以UE的重载只影响内部代码,外部动态库的内存分配使用的系统的new/delete。
所以Dev版本和Shipping版本的内存分配 机制不一样。想确认下是否如此?
Steps to Reproduce
所有UE版本都一样
Liu.Wei
(Liu.Wei)
3
Hi,
是的,但我们计划使用 UE 的 new/delete 操作符来对所有分配进行重载。我们在安卓系统上已经这样做了,因为 libc++ 可以作为静态库进行链接,但在 iOS 系统上只有动态库可用。
因此,iOS 系统存在一个已知的问题,可能会导致new/delete不匹配。
请问你是遇到了什么问题吗?
有一些客户是这样保留“StripArguments”参数的。
[Image Removed]
我们想去掉 malloc_zone_from_ptr 判断,也打算采取在strip时保留符号,使得动态库的内存分配释放 都跑UE的。不知道风险如何?
Liu.Wei
(Liu.Wei)
5
好像不能去掉malloc_zone_from_ptr,因为好像有些全局或者静态变量初始化的时间比ue的重载发生的早,所以他们的new走不到ue的重载,需要在delete的时候,仍然走系统的free。
[Image Removed]demo测试看,绑定符号 在全局变量初始化 之前,全局变量初始化 使用 new,还是使用主程序 重载的 new delete。帮确认下。
Liu.Wei
(Liu.Wei)
7
Hi,
这里好像走的是系统的分配,不是引擎的重载?引擎的重载应该是走到GMalloc的分配器里。但是等程序启动后,这个指针如果用delete释放,应该会走到GMalloc里,如果不用malloc_zone_from_ptr检查,应该会出问题。
demo 是 自己写的几行代码,包括:
一个主程序 M,同UE一样,重载 new delete方法。
一个动态库 D,动态库D包含代码 全局类变量的构造函数TestGlobalAlloc,调用new分配内存。
上图是调试主程序M,在动态库D被load进来 全局变量初始化时调用 主程序的 new(即FMemory::Malloc)。所以 符号替换机制 比 全局变量初始化更早(这个官方资料还没有找到),demo看是这样的结论:替换new delete符号后,全局的所有代码的内存分配释放 都会 走 主程序的。
Liu.Wei
(Liu.Wei)
11
可以试一下18.4的设备,因为之前有几个客户反馈在这种设备上遇到了new delete不对其的问题,修复方式是给UE定义的内存分配函数在iOS平台下都加上__attribute__((weak))的属性标签。
#if PLATFORM_IOS
#define WEAK_SYM __attribute__((weak))
#else
#define WEAK_SYM
#endif
#if !FORCE_ANSI_ALLOCATOR
#define REPLACEMENT_OPERATOR_NEW_AND_DELETE \
OPERATOR_NEW_MSVC_PRAGMA void* WEAK_SYM operator new ( size_t Size ) OPERATOR_NEW_THROW_SPEC { return FMemory::Malloc( Size ); } \
OPERATOR_NEW_MSVC_PRAGMA void* WEAK_SYM operator new[]( size_t Size ) OPERATOR_NEW_THROW_SPEC { return FMemory::Malloc( Size ); } \
OPERATOR_NEW_MSVC_PRAGMA void* WEAK_SYM operator new ( size_t Size, const std::nothrow_t& ) OPERATOR_NEW_NOTHROW_SPEC { return FMemory::Malloc( Size ); } \
OPERATOR_NEW_MSVC_PRAGMA void* WEAK_SYM operator new[]( size_t Size, const std::nothrow_t& ) OPERATOR_NEW_NOTHROW_SPEC { return FMemory::Malloc( Size ); } \
void WEAK_SYM operator delete ( void* Ptr ) OPERATOR_DELETE_THROW_SPEC { FMemory::Free( Ptr ); } \
void WEAK_SYM operator delete[]( void* Ptr ) OPERATOR_DELETE_THROW_SPEC { FMemory::Free( Ptr ); } \
void WEAK_SYM operator delete ( void* Ptr, const std::nothrow_t& ) OPERATOR_DELETE_NOTHROW_SPEC { FMemory::Free( Ptr ); } \
void WEAK_SYM operator delete[]( void* Ptr, const std::nothrow_t& ) OPERATOR_DELETE_NOTHROW_SPEC { FMemory::Free( Ptr ); } \
void WEAK_SYM operator delete ( void* Ptr, size_t Size ) OPERATOR_DELETE_THROW_SPEC { FMemory::Free( Ptr ); } \
void WEAK_SYM operator delete[]( void* Ptr, size_t Size ) OPERATOR_DELETE_THROW_SPEC { FMemory::Free( Ptr ); } \
void WEAK_SYM operator delete ( void* Ptr, size_t Size, const std::nothrow_t& ) OPERATOR_DELETE_NOTHROW_SPEC { FMemory::Free( Ptr ); } \
void WEAK_SYM operator delete[]( void* Ptr, size_t Size, const std::nothrow_t& ) OPERATOR_DELETE_NOTHROW_SPEC { FMemory::Free( Ptr ); }
#else
#define REPLACEMENT_OPERATOR_NEW_AND_DELETE
#endif
好的,我找18.4的系统测试下。
__attribute__((weak)) 是把 oprator new 等 变弱符号,这样就不会替换其它动态库的oprator new 吧。
上面说的:有一些客户是这样保留“StripArguments”参数的。这样 oprator new等符号就保留,会替换替换其它动态库的 oprator new,使得动态库的内存分配 走的 UE的,对吧。
想了解下 有人这样处理后,有遇到过问题吗?
Liu.Wei
(Liu.Wei)
14
“__attribute__((weak)) 是把 oprator new 等 变弱符号,这样就不会替换其它动态库的oprator new 吧”
是的,我是这么理解的。
客户遇到的问题有三种:
- 使用MallocBinned的项目,因为MallocBinned有malloc_zone_from_ptr判断,通常没有问题,但是在不保留符号的情况下,也会遇到使用std::string遇到new/delete不匹配问题,可以通过保留符号解决。
- 使用MallocBinne2的项目,因为没有malloc_zone_from_ptr,所以更容易遇到std::string new/delete不匹配的问题。有些客户自己支持了malloc_zone_from_ptr,有些客户可能直接使用Ansi分配器来规避。
- 即使是使用MallocBinned,并且保留符号的情况下,在IOS18.4的设备上,仍然遇到了std::string new/delelte不匹配的问题,用上面weak的方式,可以规避这个问题。
目前已知的情况和解决方案大致就是这样。