Unreal项目中AWS C++ SDK GetObject析构触发段错误求助
AWS C++ SDK在Unreal中S3 GetObject销毁时崩溃问题排查
问题描述
在Unreal Engine项目中使用AWS C++ SDK访问S3存储桶,SDK已通过Aws::Utils::Memory::MemorySystemInterface接入Unreal的内存分配器。当指定响应流类型的GetObject请求超出作用域销毁时,在Aws::Delete()中触发段错误。
请求代码如下:
Aws::S3::Model::GetObjectRequest request; request.SetBucket(bucket); request.SetKey(filename); request.SetResponseStreamFactory([destination_file]() {return Aws::New<Aws::FStream>("getdata", destination_file, std::ios_base::out | std::ios_base::binary); }); // 怀疑此代码行引发问题 Aws::S3::Model::GetObjectOutcome outcome = s3.GetObject(request); if (!outcome.IsSuccess()) { Completed.Broadcast(fullpath, false); return; } else { fullpath = to_file; Completed.Broadcast(fullpath, true); return; } // request和outcome在此处离开作用域,触发异常
请求执行成功(outcome.IsSuccess()为true)且文件已正确生成,但当outcome或request离开作用域时崩溃,错误信息:
Exception thrown at 0x00007FFA0CC2612A (vcruntime140.dll) in UnrealEditor.exe: 0xC0000005: Access violation reading location 0x0000000000000068.
调用栈:
vcruntime140.dll!00007ffa0cc2612a() Unknown [Inline Frame] aws-cpp-sdk-core.dll!Aws::Delete(std::basic_iostream<char,std::char_traits<char>> * pointerToT) Line 117 C++ aws-cpp-sdk-core.dll!Aws::Utils::Stream::ResponseStream::ReleaseStream() Line 62 C++ aws-cpp-sdk-core.dll!Aws::Utils::Stream::ResponseStream::~ResponseStream() Line 54 C++ UnrealEditor-Plugin.dll!UPlugin::Activate() Line 56 C++
异常触发于AwsMemory.h中的代码:
template<typename T> typename std::enable_if<std::is_polymorphic<T>::value>::type Delete(T* pointerToT) { if (pointerToT == nullptr) { return; } // deal with deleting objects that implement multiple interfaces // see casting to pointer to void in http://en.cppreference.com/w/cpp/language/dynamic_cast // https://stackoverflow.com/questions/8123776/are-there-practical-uses-for-dynamic-casting-to-void-pointer // NOTE: on some compilers, calling the destructor before doing the dynamic_cast affects how calculation of // the address of the most derived class. void* mostDerivedT = dynamic_cast<void*>(pointerToT); // <-- exception pointerToT->~T(); Free(mostDerivedT); }
ReleaseStream()和ResponseStream()是Aws::Delete的包装,ReleaseStream会先刷新流再调用Delete。无迹象表明pointerToT在dynamic_cast前失效,但实际出现问题。所有操作在单线程执行,且已验证destination_file等本地变量有效。项目中其他未指定流工厂的S3请求(List、Put、Get)均正常。
问题原因及解决方案
核心原因
问题根源是跨模块内存管理与多态对象的不兼容:
- 通过
Aws::New<Aws::FStream>创建流对象时,使用的是绑定到Unreal的内存分配器,分配的内存属于Unreal模块(UnrealEditor.exe或插件dll); Aws::FStream是AWS SDK定义的多态类,其虚函数表(vtable)由aws-cpp-sdk-core.dll维护;- 当SDK执行
dynamic_cast<void*>(pointerToT)时,需要读取对象的vtable来定位最派生类的内存地址,但此时Unreal的内存分配器可能已经将该内存块标记为释放或复用,导致访问非法地址0x68(通常是vtable偏移后的无效内存位置)。
其他未指定流工厂的请求正常,是因为这些请求使用SDK内部默认的流实现,内存分配与释放都在SDK模块内完成,不存在跨模块的多态对象销毁问题。
解决方案
- 改用Unreal原生文件流包装AWS响应流:
避免直接使用Aws::FStream,转而用Unreal的文件系统接口创建写句柄,再包装成AWS兼容的响应流,确保内存管理在同一模块内完成:request.SetResponseStreamFactory([destination_file]() { FString unrealDestPath = FString(destination_file.c_str()); IFileHandle* fileHandle = IFileManager::Get().CreateFileWriter(*unrealDestPath, FILEWRITE_BINARY); if (!fileHandle) { return nullptr; } // 使用PreallocatedStreamBuf包装Unreal文件句柄,自定义销毁逻辑 return Aws::New<Aws::Utils::Stream::PreallocatedStreamBuf>( "S3ResponseStream", reinterpret_cast<Aws::Utils::Stream::PreallocatedStreamBuf::UnderlyingBufferType>(fileHandle->GetNativeHandle()), 0, // 无需预分配缓冲区,直接写入文件 [fileHandle](Aws::Utils::Stream::PreallocatedStreamBuf::UnderlyingBufferType) { delete fileHandle; } ); }); - 确保内存分配释放的模块一致性:
如果必须使用Aws::FStream,则需要临时切换回SDK默认的内存分配器创建对象,销毁时也使用SDK的分配器,但这种方式会破坏Unreal的内存跟踪机制,不推荐在正式项目中使用。 - 升级AWS SDK版本:
部分旧版本AWS C++ SDK在跨模块多态对象销毁时存在bug,升级到最新稳定版可能修复该问题。
内容的提问来源于stack exchange,提问作者Soren Saville Scott
相关产品推荐
相关产品推荐

