RealDWG中AcDbMText::text()内存无法释放问题及替代方案问询
背景
我开发了一个长期运行的RealDWG索引服务,需连续打开大量DWG文件,并通过AcDbMText::text(AcString&)提取AcDbMText实体中的纯文本。
通过AcDbDatabase::readDwgFile()从文件加载的MText,每次调用text()都会占用内存,且这些内存无法通过pEntity->close()、销毁所属AcDbDatabase或文档化实体生命周期中的其他操作释放,仅能在进程关闭时通过acdbCleanUp()释放。在连续处理大量文件的场景下,内存会在数分钟内累积至数GB。
我希望了解是否有官方支持的进程内内存释放方式,或改用contentsRTF()是否为可靠的长期替代方案。
环境
- RealDWG 25.1.72.0.0(AutoCAD 2025 SDK)
- Windows 11 Enterprise(10.0.26200)
- MSVC v14.4(Visual Studio 2022),C++17,x64控制台应用
- 在Debug(/MDd /Od)和Release(/MD /O2)模式下均可复现
最小复现步骤
生成包含一个AcDbMText的小型.dwg文件,循环读取并调用text():
// 1) 生成包含一个MText的小型.dwg文件 { AcDbDatabase genDb(Adesk::kTrue, Adesk::kFalse); AcDbBlockTable* bt = nullptr; genDb.getBlockTable(bt, AcDb::kForRead); AcDbBlockTableRecord* ms = nullptr; bt->getAt(L"*Model_Space", ms, AcDb::kForWrite); bt->close(); AcDbMText* mt = new AcDbMText(); std::wstring contents; for (int i = 0; i < 2000; ++i) contents += L"{\fArial|b0|i0|c0|p34;The quick brown fox jumps over the lazy dog.} "; mt->setContents(contents.c_str()); AcDbObjectId id; ms->appendAcDbEntity(id, mt); mt->close(); ms->close(); genDb.saveAs(L"generated.dwg"); } // 2) 读取文件并循环调用text(),私有内存持续增长 for (int i = 0; i < N; ++i) { AcDbDatabase db(Adesk::kFalse); host.setWorkingDatabase(&db); db.readDwgFile(L"generated.dwg"); AcDbBlockTable* bt = nullptr; db.getBlockTable(bt, AcDb::kForRead); AcDbBlockTableIterator* it = nullptr; bt->newIterator(it); while (!it->done()) { AcDbBlockTableRecord* rec = nullptr; it->getRecord(rec, AcDb::kForRead); AcDbBlockTableRecordIterator* rit = nullptr; rec->newIterator(rit); while (!rit->done()) { AcDbEntity* ent = nullptr; rit->getEntity(ent, AcDb::kForRead); if (auto* m = AcDbMText::cast(ent)) { AcString out; m->text(out); // <-- 内存在此处增长 } ent->close(); rit->step(); } delete rit; rec->close(); it->step(); } delete it; bt->close(); host.setWorkingDatabase(nullptr); // db析构函数在此执行,但未释放增长的内存 } acdbCleanUp(); // 仅此操作可释放内存
所有实体所有权均遵循文档化模式(getEntity → close → AcDbDatabase析构函数),但db析构函数每次迭代执行后并未释放内存。
观察到的行为
对文件加载的MText调用text(),执行300次迭代,通过外部进程采样Process.PrivateMemorySize64:
- 内存从7.5 MB增长至603 MB
- 内存增长严格单调且线性(此负载下每次迭代约增长2 MB),无平台期或下降
- 单次调用内存开销随内容大小增加,且会被格式代码(\f、\C、\H等)放大3-14倍
基于相同生成的.dwg和负载的对比测试:
- 改用
contentsRTF(AcString&):内存稳定,从8.0 MB增长至10.0 MB,约2秒完成 - 内存中创建的MText(
new AcDbMText()+setContents())调用text():无内存增长
内存增长主要来自循环后通过VirtualQueryEx可见的约16 MB MEM_PRIVATE AllocationBase区域。Heap32ListFirst显示前后堆列表一致,说明这些内存并非Win32堆,而是通过VirtualAlloc直接预留的内部池/内存区。
已排除的情况
- 测试代码所有权问题:将
text()替换为返回eOk+空AcString的桩函数后,内存增长完全消失 - 文件内容问题:16 KB的DWG由复现代码生成,无扩展字典、X数据或外部字体引用
- 缺失字体/资源:Instrumented findFile()回调显示
text()调用期间无.shx/.ttf文件查找;调整MTEXTMAP.INI无影响 - 缓冲区溢出:内存增长单调且成比例,无崩溃;16 MB的内存块分配模式不符合溢出特征
反汇编确认text()和contentsRTF()是acdb25.dll中完全不同的内部模块的轻量转接函数。
问题
AcDbMText::text()是否会填充超出实体和所属数据库生命周期的进程级缓存,且该行为有文档说明或符合预期?- 若存在上述缓存,是否有官方支持的API可在进程运行中释放该内存,无需使用会销毁整个RealDWG宿主的
acdbCleanUp()? - 改用
contentsRTF()并在后续处理中剥离RTF代码是否为可靠的长期替代方案?contentsRTF()是否存在类似的内部缓存?
注:咨询的是官方支持的生命周期/缓存API,而非确认是否为bug。
内容的提问来源于stack exchange,提问作者Mykola Lysenko

