MemoryMallocFree

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版本都一样

Hi,

是的,但我们计划使用 UE 的 new/delete 操作符来对所有分配进行重载。我们在安卓系统上已经这样做了,因为 libc++ 可以作为静态库进行链接,但在 iOS 系统上只有动态库可用。

因此,iOS 系统存在一个已知的问题,可能会导致new/delete不匹配。

请问你是遇到了什么问题吗?

有一些客户是这样保留“StripArguments”参数的。

[Image Removed]

我们想去掉 malloc_zone_from_ptr 判断,也打算采取在strip时保留符号,使得动态库的内存分配释放 都跑UE的。不知道风险如何?

好像不能去掉malloc_zone_from_ptr,因为好像有些全局或者静态变量初始化的时间比ue的重载发生的早,所以他们的new走不到ue的重载,需要在delete的时候,仍然走系统的free。

[Image Removed]demo测试看,绑定符号 在全局变量初始化 之前,全局变量初始化 使用 new,还是使用主程序 重载的 new delete。帮确认下。

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符号后,全局的所有代码的内存分配释放 都会 走 主程序的。

请问你是在18.4的设备上测试的吗?

17.4.1

可以试一下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的,对吧。

想了解下 有人这样处理后,有遇到过问题吗?

“__attribute__((weak)) 是把 oprator new 等 变弱符号,这样就不会替换其它动态库的oprator new 吧”

是的,我是这么理解的。

客户遇到的问题有三种:

  1. 使用MallocBinned的项目,因为MallocBinned有malloc_zone_from_ptr判断,通常没有问题,但是在不保留符号的情况下,也会遇到使用std::string遇到new/delete不匹配问题,可以通过保留符号解决。
  2. 使用MallocBinne2的项目,因为没有malloc_zone_from_ptr,所以更容易遇到std::string new/delete不匹配的问题。有些客户自己支持了malloc_zone_from_ptr,有些客户可能直接使用Ansi分配器来规避。
  3. 即使是使用MallocBinned,并且保留符号的情况下,在IOS18.4的设备上,仍然遇到了std::string new/delelte不匹配的问题,用上面weak的方式,可以规避这个问题。

目前已知的情况和解决方案大致就是这样。