fix: 修复 AecDbEwEquipment 的内存泄漏(夹点数组、几何单元、剖切与投影克隆)
This commit is contained in:
1 parent
55391b8dc8
commit
03bbe08b76
2 files changed
+164
-80
No files matched your search
@@ -109,7 +109,9 @@ void FreeBuffer(CPtrArray& arSubPrimPtrs) // :1520
|
||||
|
||||
被 `copyFrom:2181` 调用,于是 `copyFrom` 每次都泄漏被覆盖的那批 `XPrimData` 结构体。修 2.2 时会连带放大这个问题。
|
||||
|
||||
另外这里无条件 `delete[](byte*)`,而 `releaseData()` 是按类型区分 `delete[]` / `delete` 的。`byJson`(`:820`)用 `new PVOID[]` 分配,属于数组,`delete[]` 恰好对;但若将来有类型用标量 `new`,这里就是堆损坏。**建议统一改成 `pData->releaseData(); DELETE_PTR(pData);`**,让释放规则只有一处。
|
||||
另外这里无条件 `delete[](byte*)`,而 `releaseData()` 是按类型区分 `delete[]` / `delete` 的。`byJson`(`:820`)用 `new PVOID[]` 分配,属于数组,`delete[]` 恰好对。
|
||||
|
||||
**修复时没有改用 `releaseData()`**:核实后发现 `releaseData()`(`EarThwormDef.h:2544`)的 `default` 分支用的是标量 `delete`,而 `m_pData` 的全部分配点(`copyXPrimData:150`、`dwgInFieldsForXPrim:193`、`byJson:820`、`:4472`)都是 `new byte[]` / `new PVOID[]`,标量 `delete` 反而与分配不匹配。所以本次统一走 `delete[](byte*)` + `DELETE_PTR(pData)`,封装成 `ReleaseXPrimData()` 供四处复用。`releaseData()` 自身这个不一致留待单独处理(它影响的是所有 `XPrimData` 的释放路径,不只是本文件)。
|
||||
|
||||
### 2.4 P0:`subDMesh2NewSolid()` 删错元素,且泄漏
|
||||
|
||||
@@ -193,19 +195,48 @@ delete pEnt; // :4049 只删了 exp
|
||||
|
||||
### 2.12 内存问题汇总
|
||||
|
||||
| 级别 | 问题 | 位置 | 触发频率 | 单次量级 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| P0 | 夹点数组重复分配 | `:1707` | 每个实体每次开图 | ~300 KB |
|
||||
| P0 | 析构不释放几何单元 | `:1686` | 每个实体销毁 | 全部参数块 |
|
||||
| P0 | `FreeBuffer` 不删结构体 | `:1520` | 每次 `copyFrom` | N 个结构体 |
|
||||
| P0 | `subDMesh2NewSolid` 删错+泄漏 | `:3159` | 含 SubDMesh 的图 | N 个单元 + 几何错乱 |
|
||||
| P1 | `RemoveSubPrimObj*` 漏 `releaseData` | `:4276` `:4291` | 编辑删除单元 | 1 个参数块 |
|
||||
| P1 | `GetDraw2dables` 克隆泄漏 | `:2850` `:2857` | 2D 导出/剖切 | N 个 solid |
|
||||
| P1 | 剖切克隆泄漏 | `:12691` `:12620` | 每次剖切 | N 个实体 |
|
||||
| P1 | `readString` 未释放 | `:1720` | 每次开图 | 小 |
|
||||
| P1 | `convertToRegion` 泄漏 | `:4032` | 最近点查询 | N 个 region |
|
||||
| P2 | 空曲线丢弃 | `:11240` | 每次 2D 投影 | 小 |
|
||||
| P2 | `ByJson` / `MakeBlockRefSet` 失败路径 | `:12853` `:12540` | 异常路径 | 小 |
|
||||
| 级别 | 问题 | 位置 | 触发频率 | 单次量级 | 状态 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| P0 | 夹点数组重复分配 | `:1707` | 每个实体每次开图 | ~300 KB | 已修 |
|
||||
| P0 | 析构不释放几何单元 | `:1686` | 每个实体销毁 | 全部参数块 | 已修 |
|
||||
| P0 | `FreeBuffer` 不删结构体 | `:1520` | 每次 `copyFrom` | N 个结构体 | 已修 |
|
||||
| P0 | `subDMesh2NewSolid` 删错+泄漏 | `:3159` | 含 SubDMesh 的图 | N 个单元 + 几何错乱 | 已修 |
|
||||
| P1 | `RemoveSubPrimObj*` 漏 `releaseData` | `:4276` `:4291` | 编辑删除单元 | 1 个参数块 | 已修 |
|
||||
| P1 | `GetDraw2dables` 克隆泄漏 | `:2850` `:2857` | 2D 导出/剖切 | N 个 solid | 已修 |
|
||||
| P1 | 剖切克隆泄漏 | `:12691` `:12620` | 每次剖切 | N 个实体 | 已修 |
|
||||
| P1 | `readString` 未释放 | `:1720` | 每次开图 | 小 | 已修 |
|
||||
| P1 | `convertToRegion` 泄漏 | `:4032` | 最近点查询 | N 个 region | 已修 |
|
||||
| P1 | `GetDraw3dables` 合并后不删入参 | `:2975` | 每次导出 3D | N 个 solid | 已修 |
|
||||
| P2 | 空曲线丢弃 | `:11240` | 每次 2D 投影 | 小 | 已修 |
|
||||
| P2 | `ByJson` / `MakeBlockRefSet` 失败路径 | `:12853` `:12540` | 异常路径 | 小 | 已修 |
|
||||
|
||||
“已修”的改动见 2.13。
|
||||
|
||||
### 2.13 第一批内存修复的实施记录
|
||||
|
||||
改动集中在 `CoreLibrary/AecDbEwEquipment.cpp`,不涉及头文件和存储格式。
|
||||
|
||||
| # | 实际改法 | 与原方案的差异 |
|
||||
| --- | --- | --- |
|
||||
| 1 | 删掉 `dwgInFields` 里的 `new AecGripData[GRIP_MAXNUM]`;`readString` 后补 `acutDelString(pszTmp)` | 同原方案 |
|
||||
| 2 | 析构补 `FreeBuffer(m_arSubPrimPtrs)` | 同原方案 |
|
||||
| 3 | 新增 `ReleaseXPrimData()`(匿名 namespace),`FreeBuffer(CPtrArray&)` 改为调用它 | 没用 `releaseData()`,原因见 2.3 |
|
||||
| 4 | `subDMesh2NewSolid()` 按 `idxs` 倒序 `ReleaseXPrimData` + `RemoveAt` | 同原方案,另把 `size_t i` 改成 `int i` 倒序 |
|
||||
| 5 | `RemoveSubPrimObj` / `RemoveSubPrimObjByID` 改用 `ReleaseXPrimData` | 同原方案 |
|
||||
| 6 | `GetDraw2dables` 两个 `continue` 前补 `DELETE_PTR(pClnEnt)` | 同原方案 |
|
||||
| 7 | `OldGetSectionDraw` 循环内补 `DELETE_PTR(pSubCln)`、循环后补 `FreeBuffer(arSubPtrs)`;`GetSectionDraw` 两处 `return OldGetSectionDraw` 前补 `delete pFit` | 同原方案 |
|
||||
| 8 | `convertToRegion` 的 `regions` 循环结束后逐个 `DELETE_PTR`;顺带把内层遮蔽的 `i` 改为 `j` | 同原方案 |
|
||||
| 9 | `SortAecComplexCurves` 空曲线改为 `DELETE_PTR` | 同原方案,另补 `pCrv == NULL` 判断 |
|
||||
| 10 | `ByJson` 循环前 `FreeBuffer(m_arSubPrimPtrs)`,`byJson` 失败时 `ReleaseXPrimData(p)` | 同原方案 |
|
||||
| 11 | `MakeBlockRefSet` 的 `getBlockTable` / `pTbl->add` 失败时 `pRec->close()` 并返回 `false` | 同原方案 |
|
||||
| 12 | `dwgInFields` 的 `m_arSubPrimPtrs.RemoveAll()` 改为 `FreeBuffer` | 新增,原注释写着 FreeBufer 但没真释放 |
|
||||
| 13 | `GetDraw3dables` 的 `booleanOper(kBoolUnite)` 之后补 `DELETE_PTR(pSolid)` | 新增,见 3.15 |
|
||||
|
||||
**尚未处理**:
|
||||
|
||||
- 2.8 的第二个问题(为了算最近点把临时 solid 写进模型空间再 erase),属于副作用与性能,放在第三批
|
||||
- `~XPrimData()`(`EarThwormDef.h:2539`)只删 `m_Combins` 里的结构体,不释放它们的 `m_pData`。这条要动公共头文件,且只影响 `kCombination` 单元(存盘时又会被 3.1 清空),单独评估
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -365,11 +396,16 @@ for (...) {
|
||||
|
||||
| 位置 | 写法 | 若 SDK 会删除入参 | 若 SDK 不删除入参 |
|
||||
| --- | --- | --- | --- |
|
||||
| `:3774` | `booleanOper(...); DELETE_PTR(p);` | 二次释放 | 正确 |
|
||||
| `:3867` `:3964` | `booleanOper(...)` 后统一 `deleteArrayVoidPtrs` | 二次释放 | 正确 |
|
||||
| `:2964` | `booleanOper(kBoolUnite, pSolid)`,克隆体不再处理 | 正确 | 每次泄漏一个克隆 |
|
||||
| `:3790` | `booleanOper(...); DELETE_PTR(p);` | 二次释放 | 正确 |
|
||||
| `:3883` `:3980` | `booleanOper(...)` 后统一 `deleteArrayVoidPtrs` | 二次释放 | 正确 |
|
||||
| `:14198` | `booleanOper(...); DELETE_PTR(pSolid);` | 二次释放 | 正确 |
|
||||
| `:2975` | `booleanOper(kBoolUnite, pSolid)`,克隆体不再处理 | 正确 | 每次泄漏一个克隆 |
|
||||
|
||||
也就是说:**无论 SDK 语义是哪一种,这三处至少有一处是错的**。需要先用最小样例(两个 box 做 unite,然后只删第二个)在调试版 + Application Verifier 下确认,再统一写法。这条不确认,2.6 和 2.7 的修复方式都不好定。
|
||||
本文件 5 处调用里 4 处都主动释放入参,`:2975` 是唯一的例外。
|
||||
|
||||
**2026-10-23 结论**:按「`booleanOper` 不删除入参」处理,`:2975` 补 `DELETE_PTR(pSolid)`(见 2.13 #13)。依据是同一文件另外 4 处(含失败路径 `:2002` 的 `DELETE_PTR(pSolidInside)`)都按调用方负责释放来写;若 SDK 实际会删除入参,这些既有代码早就在正常流程里二次释放崩掉了。
|
||||
|
||||
另外 `CoreLibrary/AcDbX3dSolid.cpp:1998` 的 `booleanOper` 成功路径没有释放 `pSolidInside`,同一约定下也是一个泄漏点,不在本次范围。
|
||||
|
||||
### 3.16 其他
|
||||
|
||||
@@ -468,21 +504,23 @@ Draw3dSolid(&dc, nColor); // :3062 拖拽时每帧都走到这里
|
||||
|
||||
### 5.1 第一批:内存与正确性(不改接口、不改格式)
|
||||
|
||||
| # | 改动 | 位置 | 风险 |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | 删掉 `dwgInFields` 里的重复分配,`readString` 补 `acutDelString` | `:1707` `:1720` | 低 |
|
||||
| 2 | 析构补 `FreeBuffer(m_arSubPrimPtrs)` | `:1686` | 低(依赖 #3) |
|
||||
| 3 | `FreeBuffer(CPtrArray&)` 改为 `releaseData()` + `DELETE_PTR` | `:1520` | 低 |
|
||||
| 4 | `subDMesh2NewSolid` 按下标倒序删除并释放 | `:3159` | 中(涉及几何正确性,需样例图) |
|
||||
| 5 | `RemoveSubPrimObj*` 补 `releaseData()` | `:4276` `:4291` | 低 |
|
||||
| 6 | `subErase` 的 `Find` 补 `!= -1` | `:2119` | 低(行为会变,需回归删除阀门) |
|
||||
| 7 | `DrawHandFlg` 的 `==` 改 `=` | `:2656` | 低 |
|
||||
| 8 | `ProcessHlrResult` 改用 `it = erase(it)` | `:13128` | 低 |
|
||||
| 9 | `copyFrom` 补 `m_bVert` / `m_idDash` / `m_idCenter` | `:2174` | 低 |
|
||||
| 10 | `GetSectionDraw` 返回值改 `nCoundPre - nCount` | `:12762` | 低(需确认调用方语义) |
|
||||
| 11 | `clearGeoBuffer` 实现为清 2D/3D 缓存 | `:12861` | 低 |
|
||||
拆成两批提交:**内存问题已做(见 2.13)**,逻辑缺陷待做。
|
||||
|
||||
这一批做完,开图内存和几个明确的逻辑错误就都清掉了,且不触碰存储格式。
|
||||
| # | 改动 | 位置 | 风险 | 状态 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 1 | 删掉 `dwgInFields` 里的重复分配,`readString` 补 `acutDelString` | `:1707` `:1720` | 低 | 已完成 |
|
||||
| 2 | 析构补 `FreeBuffer(m_arSubPrimPtrs)` | `:1686` | 低(依赖 #3) | 已完成 |
|
||||
| 3 | `FreeBuffer(CPtrArray&)` 释放 `m_pData` 后删结构体 | `:1520` | 低 | 已完成(未用 `releaseData`,见 2.3) |
|
||||
| 4 | `subDMesh2NewSolid` 按下标倒序删除并释放 | `:3159` | 中(涉及几何正确性,需样例图) | 已完成 |
|
||||
| 5 | `RemoveSubPrimObj*` 补释放 `m_pData` | `:4276` `:4291` | 低 | 已完成 |
|
||||
| 6 | `subErase` 的 `Find` 补 `!= -1` | `:2119` | 低(行为会变,需回归删除阀门) | 待做 |
|
||||
| 7 | `DrawHandFlg` 的 `==` 改 `=` | `:2656` | 低 | 待做 |
|
||||
| 8 | `ProcessHlrResult` 改用 `it = erase(it)` | `:13128` | 低 | 待做 |
|
||||
| 9 | `copyFrom` 补 `m_bVert` / `m_idDash` / `m_idCenter` | `:2174` | 低 | 待做 |
|
||||
| 10 | `GetSectionDraw` 返回值改 `nCoundPre - nCount` | `:12762` | 低(需确认调用方语义) | 待做 |
|
||||
| 11 | `clearGeoBuffer` 实现为清 2D/3D 缓存 | `:12861` | 低 | 待做 |
|
||||
|
||||
另外补做的 4 处内存修复(2.6、2.7、2.9、2.10、2.11、`GetDraw3dables` 合并泄漏、`dwgInFields` 的 `RemoveAll`)见 2.13。
|
||||
|
||||
### 5.2 第二批:`m_Combins` 存盘清空(需要确认影响面)
|
||||
|
||||
@@ -507,7 +545,7 @@ Draw3dSolid(&dc, nColor); // :3062 拖拽时每帧都走到这里
|
||||
|
||||
### 5.4 需要先确认的事
|
||||
|
||||
1. `booleanOper` 是否删除入参(3.15)—— 决定 2.6 / 2.7 怎么修
|
||||
1. ~~`booleanOper` 是否删除入参(3.15)~~ —— 2026-10-23 按「不删除入参」处理,理由见 3.15
|
||||
2. `GetSectionDraw` 的返回值调用方到底期望什么(3.5)
|
||||
3. `PreprocessForHlr` 的 `mat4` 双次变换是否刻意(3.12)
|
||||
4. `kCombination` 单元的实际存量(5.2)
|
||||
@@ -519,7 +557,8 @@ Draw3dSolid(&dc, nColor); // :3062 拖拽时每帧都走到这里
|
||||
| 改动 | 验证方式 |
|
||||
| --- | --- |
|
||||
| 夹点数组 / 析构释放 | 打开含 1000+ 设备的图纸,任务管理器看私有字节;关图后应回落 |
|
||||
| `FreeBuffer` 改 `releaseData` | 各类单元(box/cylinder/cone/Revit/组合)各删一个,确认无崩溃 |
|
||||
| `FreeBuffer` 释放结构体 | 各类单元(box/cylinder/cone/Revit/组合)各删一个,确认无崩溃 |
|
||||
| 剖切 / 2D 导出 | 对含阀门、法兰、普通设备的图各剖一次,反复开关剖切对话框,内存不持续上涨 |
|
||||
| `subDMesh2NewSolid` | 含 SubDMesh 的样例图,开图后单元数量与类型正确,绘制不缺体 |
|
||||
| `subErase` | 删除阀门/接头/普通设备各一次,确认只有阀门走合并管线 |
|
||||
| `HLR_CALC` | 剖切过程中保存再重开,设备 3D 仍可见 |
|
||||
@@ -541,8 +580,8 @@ Draw3dSolid(&dc, nColor); // :3062 拖拽时每帧都走到这里
|
||||
|
||||
## 8. 实施顺序
|
||||
|
||||
1. 第一批 1~5(内存),单独一个 PR,可独立验证
|
||||
1. ~~第一批 1~5(内存),单独一个 PR,可独立验证~~ —— 已完成,范围扩到 2.1~2.11 全部内存项,见 2.13
|
||||
2. 第一批 6~11(逻辑),单独一个 PR
|
||||
3. 确认 5.4 的四个问题
|
||||
3. 确认 5.4 剩下的三个问题
|
||||
4. 第二批(`m_Combins`),视存量决定是否做
|
||||
5. 第三批按 4.7 度量结果排序,一项一个 PR
|
||||
Reference in new issue
Block a user