You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RealDWG中AcDbMText::text()内存无法释放问题及替代方案问询

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中完全不同的内部模块的轻量转接函数。

问题

  1. AcDbMText::text()是否会填充超出实体和所属数据库生命周期的进程级缓存,且该行为有文档说明或符合预期?
  2. 若存在上述缓存,是否有官方支持的API可在进程运行中释放该内存,无需使用会销毁整个RealDWG宿主的acdbCleanUp()?
  3. 改用contentsRTF()并在后续处理中剥离RTF代码是否为可靠的长期替代方案?contentsRTF()是否存在类似的内部缓存?

注:咨询的是官方支持的生命周期/缓存API,而非确认是否为bug。


内容的提问来源于stack exchange,提问作者Mykola Lysenko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.02 05:13:10