尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SAP-ABAP:程序内存优化——内表滥用、内存泄漏、大对象占用的排查与优化方案

SAP-ABAP:程序内存优化——内表滥用、内存泄漏、大对象占用的排查与优化方案 ABAP核心进阶篇120篇调试与性能优化20篇第四篇ABAP程序内存优化——内表滥用、内存泄漏、大对象占用的排查与优化方案博客标题《ABAP程序内存优化内表滥用、内存泄漏、大对象占用的排查与优化方案》博客简介讲解ABAP程序内存分配的底层逻辑结合SAT内存分析功能定位内表未释放、全局变量冗余、大对象重复实例化等内存问题分享内表初始行预分配、无用数据及时清理、对象池复用、内表数据拆分处理的优化技巧解决程序运行时内存溢出、系统资源占用过高问题。 写在前面上一篇我们把数据库性能这块啃下来了——通过ST05定位慢SQL、用FATE替代循环内SELECT、给大表加合适的索引把一个210秒的报表干到了12.5秒。但故事到这里还没完。当DB时间被优化到极致或者程序干脆是CPU密集型时下一个瓶颈就浮出水面了——内存。你一定见过这些场景报表跑到一半突然Memory Consumption has Exceeded the Limit然后Dump后台作业被管理员Kill掉SAT里80%的CPU时间花在内表操作上多用户同时跑同一个程序系统整体内存飘红。这些问题的根源都是内存使用不当。内存优化全景底层模型EM(2GB)/Heap/Paging排查工具SAT Memory Analysis反模式修复预分配/释放/分批/对象池实战案例3.2GB → 480MBWork Process 内存配额内表扩容机制快照对比定位大户按内存降序排列APPEND预分配 / FREE释放SELECT指定字段 / 对象池复用分批处理 / DELETEPACKSELECT *→指定字段一次性→分批循环NEW→对象池结果表定期PACK本篇学习目标理解ABAP Work Process内存的三层模型EM / Heap / Paging掌握SAT内存分析功能能快速定位内存热点和泄漏点看懂内表在内存里的真实布局避免APPEND滥用导致的内存碎片掌握预分配、及时清理、分批处理等内存优化核心技巧能独立排查并修复 OOMOut Of Memory问题前置阅读第二篇《核心工具入门》里SAT的基础用法、第三篇里实战案例的ST12时间分解部分。适用版本SAP NetWeaver 7.51一、ABAP内存分配底层逻辑 1.1 为什么要懂底层┌─────────────────────────────────────────────────────────────────────────────────┐ │ 你以为的内存 vs 实际的内存 │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ ❌ 你以为DATA声明 → 系统自动搞定内存 → 用完自动释放 │ │ ✅ 实际 系统有严格的内存配额Work Process内存耗尽就Dump │ │ 即使你的逻辑正确内存碎片也会让系统分配不到连续空间 │ │ │ │ ❌ 你以为内表APPEND就追加一行没什么成本 │ │ ✅ 实际 APPEND如果没有预留空间 → 每次都要重新申请更大的连续内存块 复制旧数据 │ │ 10万次APPEND 10万次内存分配 9万次数据拷贝 │ │ │ │ ❌ 你以为函数返回、内表用完 → 内存自动释放 │ │ ✅ 实际 Global Function / Class Static 变量 → 跟Work Process同寿命 │ │ Lcl变量 → 函数返回才释放但如果内表被EXPORT到Memory ID → 永远不释放 │ │ │ │ 不懂底层 不懂你的代码在干什么。 │ │ 很多内存问题不是Bug是默认行为导致的。 │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘ 1.2 Work Process内存三层模型共享内存 Shared Memory多WP共享 / 表缓冲扩展内存 EM2GB配额 / 99%的变量在这里堆内存 HeapEM不够时使用 / 易碎片分页区 Paging磁盘交换 / 极慢层级说明关键参数共享内存多个WP共享表缓冲在这里只读为主ST04/SM04查看扩展内存 EM99%的变量在此每个WP上限约2GBabap/heap_area_total堆内存 HeapEM不够时使用匿名对象/动态内表abap/heap_area_dyn分页区 Paging磁盘交换极慢优化目标永远不要走到这一步-一句话总结每个Work Process有2GB的账户额度用完就会透支到Heap再不行就Dump。 1.3 内表的内存布局最关键内表底层是数组结构 动态扩容机制扩容是指数增长的。初始 Capacity 0 APPEND 第1行 → 申请 80 bytes → Capacity 1 APPEND 第2行 → Capacity满了申请 160 bytes → 复制旧行 → 加新行 APPEND 第3行 → Capacity满了申请 320 bytes → 复制旧行 → 加新行 APPEND 第5行 → 申请 640 bytes → 复制旧4行 → 加新行 ... APPEND 第N行 → 每次翻倍扩容10万行触发约17次扩容累计拷贝超300万行数据解决方案预分配初始行 声明时预分配 DATA: gt_table TYPE TABLE OF ty_item WITH NON-UNIQUE DEFAULT KEY INITIAL SIZE 100000. 或运行时动态预分配NetWeaver 7.40 gt_table VALUE #( INITIAL SIZE lv_lines ).二、SAT内存分析实战 2.1 SAT不只是查CPU的SAT有四种分析模式其中Memory Analysis模式可做内存快照对比精准定位每个变量的内存占用。模式启动方式核心输出Time (CPU时间)默认每个函数/代码块的CPU消耗Memory AnalysisSAT → Change Mode → Memory每个变量的内存占用快照 快照对比CoverageChange Mode → Coverage代码覆盖情况Profile Parameters直接看程序内动态创建的对象/内存统计 2.2 读懂SAT内存分析结果SAT Memory Inspector 结果页 ┌──────────────┬──────────────┬──────────────┬────────────┐ │ 变量名 │ 类型 │ 当前内存 │ 内存增长 │ ├──────────────┼──────────────┼──────────────┼────────────┤ │ GT_REPORT │ TABLE (STD) │ 2.1 GB │ 2.1 GB │ │ GT_MATERIAL │ TABLE (STD) │ 384.2 MB │ 384.2 MB │ │ MO_INSTANCE1 │ OBJECT │ 156.3 MB │ 156.3 MB │ └──────────────┴──────────────┴──────────────┴────────────┘解读口诀按当前内存降序排列 → 前3名就是你的内存大户 → 重点看50MB以上的条目 → 超过1GB 必须优化。快照对比最重要的排查技巧程序执行前拍快照A → 执行关键代码段 → 拍快照B → 差值为正 新增内存差值为负 释放内存。如果函数返回后差值仍为正 → 内存泄漏三、内表滥用内存问题的头号元凶 3.1 问题一APPEND without INITIAL SIZE —— 扩容风暴写法APPEND 10万行内存分配次数累积拷贝量❌ 无预分配12,800 ms17次约300万行 × 80B ≈ 240MB✅ INITIAL SIZE 10万1,500 ms1次0拷贝✅ SELECT INTO TABLE800 ms1次直接填充原则循环内APPEND 5000行时必须预分配大内表10000行建议预分配可预估行数时如从另一个内表的LINES来直接预分配。 3.2 问题二内表没释放 —— 用完不删留着过年场景ASELECT INTO TABLE 查了10万行占100MB只处理前100行就退出了内表没清空——100MB还挂在WP上。场景B三步处理流程中Step 1查VBAK占50MB、Step 2查VBAP占150MB、Step 3关联匹配结果占200MB。Step 3完后VBAK和VBAP还在 → 总内存400MB。正确做法Step 3完后立刻FREE中间表 → 总内存从400MB降至200MB。REFRESH vs CLEAR vs FREE 选型指南指令效果适用场景CLEAR清空1行或结构内表只清表头数据行还在清空结构体REFRESH清空内表所有行释放数据内存保留表头空间内表要继续用、需要重用FREE释放所有内存连表头一起清内表彻底不用了实战口诀内表要继续用 → REFRESH内表彻底不用 → FREE循环内临时内表 → 每次REFRESH PACK大数据量中间表 → 处理完立刻FREE。 3.3 问题三内表字段冗余 —— 只取所需不取所有字段选择单行字节数100万行内存DB时间SELECT *~520 bytes520 MB2,400 ms只选5个字段~60 bytes60 MB320 ms差距8.7倍8.7倍7.5倍四、全局变量与内存泄漏看不见的隐形炸弹⚠️ 4.1 ABAP里的5种内存泄漏来源泄漏来源根因与后果Class Static 变量跟Work Process同寿命第一次执行后就一直挂着Global Function 内局部变量老版本ABAP的静态内存特性函数返回后不释放EXPORT TO MEMORY IDEXPORT了但忘了IMPORT或DELETEREFRESH ON COMMIT 未及时提交刷新缓冲被延迟数据越积越多对象实例未释放CREATE OBJECT / NEW 之后没FREE⚠️ 4.2 Class Static 变量泄漏最常见也最难防问题Class Static属性生命周期 Work Process整个生命周期。第一次NEW之后只要WP没重启内存就一直在即使你退出了当前程序也不会释放。用户A跑了1次占5MB用户B又跑了1次累积占10MB一天200个用户重复数据越来越多。修复方案一用实例属性而非Static属性随对象生命周期释放。修复方案二如果必须用Static真正需要缓存做去重大小限制——先查重已缓存就直接返回不再APPEND超过上限时REFRESH清空重来。五、大对象重复实例化构造函数的隐形成本 5.1 对象创建到底有多重写法CPU耗时内存增长泄漏的对象实例数❌ 只NEW不FREE8.2秒520 MB1000个 ❌ NEW 每次FREE但没清内部缓存15.6秒12 MB0但CPU翻倍✅ 对象池复用单次创建0.3秒52 MB0复用1个实例 5.2 对象池Object Pool复用技巧适合场景循环内需要反复使用同一个复杂对象构造函数开销大对象实例可以被重置到初始状态有RESET方法。ABAP单WP是单线程的无并发安全问题。模板代码只创建一次对象 → 循环内每次只做RESET → 用完FREE一次。对象池复用可让CPU时间减少90%省掉反复构造/析构的开销。六、内表数据拆分处理大数据量分块是王道 6.1 当数据超过内存极限怎么办每个WP的EM上限2GB加上其他变量占用实际安全线约100-200万行。超过这个量必须分块处理。 6.2 三种分块处理模式模式一SELECT分块—— 用UP TO ... ROWS OFFSET分批查。⚠️ OFFSET大时前面所有行都要跳过总行数很大时性能下降。模式二主键范围分批推荐—— 每批查完后记住最后一条主键值下一批WHERE主键 上次最后一条。DB直接根据主键定位不需要OFFSET跳过每批查询都是O(1)定位。适用主键是连续范围数字ID的表。模式三内表分批DELETE PACK—— 当必须把所有中间结果存在一个内表里时定期DELETE已处理的行 PACK TABLE释放尾部预留空间。内表删除行后底层内存不会自动收缩一定要PACK否则内存白白浪费。七、完整实战案例后台订单清算作业从3.2GB降到480MB 7.1 问题定位SAT Memory Inspector 结果按内存降序 ┌────────────────┬────────────────┬────────────────┐ │ 变量 │ 当前内存 │ 内存增长 │ ├────────────────┼────────────────┼────────────────┤ │ GT_VBAK_MONTH │ 1,040 MB │ 1,040 MB │ │ GT_VBAP_MONTH │ 1,560 MB │ 1,560 MB │ │ GT_RESULT_ALL │ 420 MB │ 420 MB │ │ MO_PRICERULE │ 160 MB │ 160 MB │ └────────────────┴────────────────┴────────────────┘ 合计3,205 MB → 超过EM上限2GB根因① GT_VBAK_MONTH GT_VBAP_MONTH一次性SELECT *了300万行字段冗余严重 ② MO_PRICERULE循环创建5次每次带100MB内部缓存→内存泄漏 ③ GT_RESULT_ALL前50万行已处理完但没DELETEPACK ④ LCL_BUFFER循环内每次REFRESH但没PACK容量空间。 7.2 四项优化落地优化项优化前优化后提升① SELECT * → 指定字段 主键范围分批2,600 MB25 MB峰值104倍② 对象池复用循环内只NEW一次5次构造1次构造CPU节省90%③ 结果分批DELETE PACK420 MB累积380 MBPACK过释放空洞④ 循环内临时表 REFRESH PACK未释放预留空间释放尾部空洞减少内存碎片 7.3 效果验证指标优化前优化后提升幅度内存峰值3,200 MB480 MB6.7倍GT_VBAK VBAP 占用2,600 MB25 MB (峰值)104倍对象实例泄漏5个 × 160MB1个无泄漏5倍APPEND 扩容次数1001次预分配彻底消除作业成功率60%100%✅ 完全解决八、内存优化检查清单 红牌级必须改循环内APPEND/INSERT → 有没有INITIAL SIZE预分配SELECT INTO TABLE大数据量(10万行) → 有没有分批处理CLASS-DATA / Static变量 → 有没有无上限增长风险CREATE OBJECT / NEW之后 → 有没有FREEEXPORT TO MEMORY ID → 有没有对应DELETE内表用完了 → 有没有FREE/REFRESH循环内临时内表 → 有没有每次REFRESH PACK 黄牌级建议改SELECT * INTO TABLE → 能不能指定字段大数据量结果表(50万行) → 有没有定期DELETE PACK声明SORTED/HASHED TABLE → 数据量够大吗查找频繁吗循环内频繁NEW同类对象 → 能不能用对象池复用DESCRIBE TABLE行数可以预知 → 有没有INITIAL SIZE预分配 常规级好习惯内表处理完部分行后 → DELETE PACK释放空间程序退出前 → SAT快照确认无异常大残留内存FUNCTION/CLASS全局变量 → 确认必要的生命周期提交测试时 → SAT Memory Analysis跑一遍记录峰值九、总结核心知识回顾无预分配用完未释放SELECT *Static泄漏循环NEW一次性加载内存优化流程STAT确认峰值SAT Memory Analysis定位大户分析原因INITIAL SIZEFREE / REFRESH指定字段改实例属性对象池复用分批处理SAT验证 回归测试编码原则和DB优化互补能用预分配就不用裸APPEND —— 省时间也省内存碎片用完FREE是基本功 —— 函数结束前回头扫一遍确保所有临时变量都FREE了CLASS-DATA要谨慎 —— 写Static之前先问自己这东西真的要跟WP同寿命吗能分块就不要一次塞 —— 超过10万行的处理先想分批方案对象池是好东西 —— 但前提是对象能被RESET成初始状态下一篇预告《业务逻辑层性能优化循环嵌套、冗余计算、低效算法的优化技巧》作者爱喝水的鱼丶版本记录2026年8月 你在ABAP内存优化中踩过哪些坑有没有用SAT内存分析发现过意想不到的内存大户欢迎分享你的优化故事。
返回列表