凌晨两点,手机震动,值班同事转来一张ST22截图,红底白字:MEMORY_NO_MORE_PAGING。系统响应已经慢到点不动,业务电话一个接一个。这种场景,做过SAP Basis的朋友应该都不陌生。
这个错误的名字看着吓人,但本质并不神秘:SAP的Paging区域被写满了。这里说的Paging,不是操作系统层面的swap,而是SAP内存管理里一个专门用来“换入换出”用户上下文的共享内存区。很多同事遇到这个转储第一反应是加物理内存,结果加完服务器内存照样报错,问题就出在没搞懂SAP自己的内存模型。
这篇文章我把MEMORY_NO_MORE_PAGING的来龙去脉一次讲清楚:ST22里怎么读错误、SAP的Paging机制到底在干什么、rdisp/PG_SHM和rdisp/PG_MAXFS两个参数应该怎么理解、以及生产环境里一次完整的排查调参过程。适合SAP Basis、ABAP开发者和系统运维人员参考,尤其是正在被ST22高频错误困扰的朋友。
1. 先看懂错误本身:MEMORY_NO_MORE_PAGING 是“换页区满了”,不是“内存不够”
1.1 从ST22转储记录里能读到什么
ST22是SAP系统查看短转储(Short Dump)的入口。双击一条MEMORY_NO_MORE_PAGING转储记录,界面会分几个区域展示信息,我建议你按这个顺序看:
- Error Analysis(错误分析):这段文本直接把原因说得很直白,大意是系统在尝试扩展Paging区域时,发现共享内存池已经达到了配置的上限。里面会提到具体的参数名,比如
rdisp/PG_SHM,但别急着去改参数,先把后面几条看完。 - What happened(发生了什么):这里能看到触发转储的ABAP程序名、事务代码(Transaction Code)、报表名或函数模块。这个信息很关键,能帮你判断错误是集中出现在某一个程序,还是全系统到处都在报。
- Source Code(源代码位置):指明程序里具体是在哪一行发生了内存分配请求。对ABAP开发同事来说,这是定位问题程序的直接线索。
- Active Requests / Work Process Info(活动请求和工作进程信息):记录触发错误时,这个工作进程正在处理什么请求,有没有RFC调用,用户会话的上下文是什么状态。
看转储记录最忌讳的事情就是只看错误名。同一个MEMORY_NO_MORE_PAGING,可能是某个报表程序申请了一块超大内表导致的,也可能是整个系统并发用户数暴涨导致的,两者处理方式完全不同,只看错误名就去改参数,大概率调不准。
1.2 Paging区耗尽与内存耗尽的本质区别
很多同事把MEMORY_NO_MORE_PAGING理解为“系统内存不足”,于是直奔机房加内存。这个方向错得很冤。
操作系统内存不足时,表现通常是新进程起不来、malloc失败、系统swap疯狂读写,甚至OOM Killer把进程杀掉。但是SAP的ABAP工作进程早就启动完毕了,常驻在那里,物理内存不足时它会先从操作系统层面感知到压力,然后系统变慢、事务卡死,但不一定立刻抛这个ST22转储。
MEMORY_NO_MORE_PAGING的触发点是SAP自身的Paging共享内存区域达到了容量上限。这个区域就像SAP内部的一块“交换分区”,专门用来存放那些暂时不活跃的用户上下文和数据。当ABAP工作进程需要把一个用户的上下文换出(roll-out),或者从Paging区换入(roll-in)时,如果Paging区放不下了,系统就直接抛出这个转储。
所以一个很重要的判断是:物理内存还有剩余,不代表SAP内部Paging区有空间。这两个层面是独立的。理解了这一点,才不会在排查时被free -g显示的“剩余几十GB”迷惑。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 绕不开的SAP内存模型:EM、Heap、Paging、Rolling 是怎么配合的
2.1 一次ABAP会话的内存分配顺序
要理解Paging,得先看SAP NetWeaver ABAP环境的内存全景。简化之后,主要包含这几块:
| 区域 | 作用 | 关键参数 |
|---|---|---|
| Extended Memory(EM) | 存放用户上下文、ABAP对象、内表数据,所有工作进程共享 | ztta/roll_extension |
| Heap Memory | 私有堆内存,当EM空间不足时,进程在自身地址空间内单独申请 | abap/heap_area_total等 |
| Paging Buffer | 存放被换出的上下文和ABAP对象,是SAP自己的“换页区” | rdisp/PG_SHM |
| Roll Area | 处理调用栈和事务回滚数据 | rdisp/ROLL_SHM |
一个ABAP会话(比如用户跑一个报表)被分配到某个dialog工作进程后,内存分配顺序大概是这样的:
- 用户上下文(User Context)优先放到扩展内存(EM)里。
- 程序运行时产生的内表、对象实例等数据,也先在EM里分配。
- EM达到上限后,系统尝试把一部分不活跃的数据“换出”到Paging区。
- 如果Paging区也满了,或者单个上下文超出上限,系统再尝试使用Heap私有堆内存。
- 私有堆内存也不是无限的,它受操作系统地址空间和
abap/heap_area_total等参数限制。
这个顺序里,Paging区承担的是“安全垫”的角色。EM是高速公路,Paging是服务区。高速公路堵了,车可以停到服务区;服务区也满了,整条路就瘫了。
2.2 Paging区到底存了什么,为什么会占满
Paging区存放的东西,本质上就是“被暂时赶出内存的ABAP对象和用户上下文”。最常见的来源有几类:
- 用户上下文(User Context):每个登录用户都有一份,包含用户参数、授权数据、当前事务状态等。当用户会话处于等待状态(比如用户看着屏幕没操作),系统可能把它的上下文暂时换出,腾出EM给活跃用户。
- ABAP对象实例:报表程序里创建的对象,如果EM压力大,也会被序列化到Paging区。
- RFC/批处理上下文:后台作业、RFC调用的上下文同样会占用Paging区,尤其是大批量作业并发时,Paging区增长非常快。
占满的常见场景,我总结为三类:
- 并发用户数瞬时暴涨:比如月底结账,几百个用户同时跑报表,每个用户上下文都要在EM和Paging之间换进换出,Paging区一下就满了。
- 单个程序上下文过大:某个ABAP报表在代码里一次性创建了巨大的内表,或者多次调用
IMPORT/EXPORT TO DATABASE之类的操作,导到系统频繁做上下文交换,把Paging区塞满。 - EM配置明显偏小:
ztta/roll_extension设置得不够大,EM早早耗尽,所有东西都被压到Paging区,Paging区成了实际的主力内存区,自然最先爆掉。
2.3 64位时代为什么还会遇到Paging问题
现在新系统基本都是64位了,地址空间早就不是瓶颈。按说EM可以设得很大,Paging机制应该没那么重要,但实际情况不是这样。
SAP的对象上下文换出换入机制仍然保留,而且Paging区仍然有独立的上限参数。也就是说,哪怕物理内存非常充足,只要rdisp/PG_SHM配置的值不大,或者Paging区的水位一路涨到上限,MEMORY_NO_MORE_PAGING照样报。
另外还有一个容易被忽视的点:SAP的内存配置默认值在标准安装包里往往偏保守。很多项目上线后几乎不调整内存参数,全靠当初的安装默认值运行。一旦并发和业务量上来,Paging区这种默认容量不太大的区域就会成为短板。
所以,64位只是扩大了容量天花板,并没有消除SAP内存管理的结构性问题。该看ST02的还得看,该算参数还得算。
3. rdisp/PG_SHM 和 rdisp/PG_MAXFS:一个管池子,一个管天花板
3.1 rdisp/PG_SHM:Paging共享池的总量
rdisp/PG_SHM定义了SAP实例中Paging共享内存池的大小。它是整个实例级别的参数,所有工作进程共享这同一个池子。可以理解为“Paging区总共能装多少货”。
这个参数有几个特点需要记住:
- 单位:不同版本文档标注不完全一致,有的写KB,有的写Byte。判断方法其实很简单:RZ11里看到参数当前值后,去ST02看Paging Buffer当前显示的容量,如果两者数字能对上,就是同一个单位;对不上就按比例换算(通常是Byte)。
- 静态参数:修改后不能在线生效,必须重启该实例。所以生产环境调这个参数,一定要提前计划维护窗口。
- 不是越大越好:Paging区占用的也是物理内存的一部分,它和EM、工作进程共享内存、操作系统的页缓存,都在抢同一台服务器的物理内存。把
PG_SHM调得过大,反而挤压了其他内存区域,轻则性能下降,重则系统整体变慢。
一个比较常见的初始配置思路:在没有历史数据可以参考时,可以用物理内存的5%到10%作为起点,然后重点观察ST02里Paging区的使用水位,根据水位再调整。注意,这只是经验值,不代表SAP官方推荐,最终要以系统的实际监控数据为准。
3.2 rdisp/PG_MAXFS:单个用户上下文的上限
rdisp/PG_MAXFS限制的是单个用户上下文(或者更准确地说,单个上下文对象)在Paging区能够占用的最大容量。可以理解为“每辆车最多能占多少个服务区车位”。
这个参数存在的意义是防止一个程序把整个Paging池子全部吃掉。设想一个报表在Paging区里写入了海量上下文数据,如果没有PG_MAXFS的限制,它可能直接把其他用户挤出Paging区,导致全系统连锁转储。
但这个参数也不是随便调大的。如果你把一个报表程序的PG_MAXFS调得很大,确实能解决这个程序自己报的MEMORY_NO_MORE_PAGING,但代价是它有可能挤压其他用户的Paging空间。所以:
- 正常情况下,保持默认值就够了。
- 如果你在ST22里反复看到同一个程序因为上下文过大而报错,且这个程序的逻辑确实需要考虑,单独评估后再调整
PG_MAXFS。 PG_MAXFS建议不要超过PG_SHM的10%到20%。如果单用户的上限占到池子的三分之一甚至一半,整个Paging区就失去了隔离效果。
3.3 参数联动:EM、PG_SHM、PG_MAXFS与roll_extension的配合
排MEMORY_NO_MORE_PAGING调到rdisp/PG_SHM就够了吗?不够。前面说过,Paging区只是EM的溢出承接区。如果EM本身太小,Paging区承接的是无限涌入的数据,怎么调都会爆。
通常我调整这类问题时,会同时检查这些参数:
ztta/roll_extension:控制扩展内存EM的大小,单位MB。如果这个值偏小,优先适当调大,让EM多扛一些数据,Paging区压力自然降下来。rdisp/ROLL_SHM:Roll区域的共享内存大小。事务回滚和调用栈相关的数据都走这里。如果这个区域也接近上限,系统的稳定性同样会出问题。排查内存类转储时,ST02里要一起看。abap/heap_area_total和abap/heap_area_dia:私有堆内存的总量和对话框进程私有堆上限。私有堆太大虽然不一定引发MEMORY_NO_MORE_PAGING,但会导致工作进程占用物理内存快速增长,反过来压缩Paging区的物理内存空间。
换句话说,只看PG_SHM是孤立地看待问题。正确的方式是把EM、Paging、Rolling、Heap看成一个系统,哪个区域水位高,就顺着内存分配链路往上找,看是上游没扛住,还是下游排泄不畅。
4. 从报警到收敛:一次完整的问题排查和参数调整过程
4.1 第0步:先恢复业务,再做诊断
生产环境出现MEMORY_NO_MORE_PAGING时,往往伴随着系统响应极慢。如果你一上来就坐下慢慢分析参数,业务人员会疯掉。我的习惯是分三步走:
- 先确认系统还能不能访问:能进ST02就进ST02,连ST02都打不开,就准备重启实例。
- 如果是重启,在重启前尽量保留现场:截图ST02的内存水位,导出ST22的转储记录,把当前登录用户数记录下来。
- 如果系统还能撑住,优先做短期缓解,再考虑根本调优。短期缓解手段包括:检查有没有异常的后台作业或RFC调用,必要时可以释放空闲用户会话,降低当前并发压力。
这一步的目标是让系统先恢复可用,而不是立刻调参数。生产环境里,稳定大于一切。
4.2 用ST02和RZ11把内存水位摸清楚
系统恢复后,进入真正的诊断阶段。按这个顺序查:
ST02(内存管理监控):
打开ST02后,界面会列出Extended Memory、Paging、Roll等区域的使用情况。核心看的是Paging区域的当前使用率和历史峰值。如果Paging区域长期处于90%以上,或者经常冲到100%,说明容量确实不够;如果只在特定时间段冲高,就要结合业务时间点看。
SM50 / SM66(工作进程和运行事务):
看当前dialog进程、更新进程、后台进程是不是全部busy。如果大量进程卡在某一个程序或者某一张数据库表的锁等待上,用户的上下文就一直挂在内存里无法释放,Paging区水位自然持续走高。
AL08(当前登录用户):
看看在线用户数是不是比平时高出一截,尤其是事件驱动型的业务(比如大批量导入、月结、年结)期间,用户数暴涨是Paging区爆掉的高发时段。
RZ11(参数查看):
把这些参数挨个看一遍当前值:rdisp/PG_SHM、rdisp/PG_MAXFS、ztta/roll_extension、rdisp/ROLL_SHM。同时注意看参数的动态属性,确认哪些需要重启生效。
做完这套检查,你会得到一张完整的“内存水位图”:哪个区域紧张、哪个参数偏小、哪个业务触发的,基本有数了。
4.3 参数调整怎么算:经验公式与前提条件
假设排查结果是Paging区域确实容量不足,且EM设置合理,那么调整rdisp/PG_SHM时,可以按这样的思路来算:
- 先看物理内存总量,减去操作系统和其他非SAP进程的占用,得出SAP实例可用内存的大概范围。
- 在SAP实例可用内存里,分别预留出工作进程、EM、Roll区、SAP共享内存(Shared Memory)的空间,剩下的才考虑分配给Paging区。
- 结合ST02当前水位,把
PG_SHM在当前值基础上提高50%到100%作为起步。不要一步到位翻好几倍,内存分配这东西,改得猛容易引发其他区域的连锁问题。 - 修改后通过RZ11写入实例Profile,保持配置持久化,避免实例重启后参数回退。
PG_MAXFS的调整逻辑略有不同。它通常只在单用户上下文过大时动。调整前先确认ST22里反复报错的是不是同一个程序,并且这个程序确实具备“占用大量内存做存储”的特征,否则不要轻易调大。
这里要注意一个前提:调整PG_SHM的前提是服务器物理内存还有余量。如果物理内存本身已经吃到90%以上,甚至操作系统swap已经出现明显波动,正确的做法是先给服务器扩容或者减少并发,而不是调大Paging区。否则Paging区调大了,运行进程的内存不够,系统崩溃得会更快。
4.4 调整后的验证与持续推进
参数调完、实例重启后,事情并没有结束。我一般会用一周到两周的时间持续观察:
- ST02里Paging区的水位是否回落到安全区间(比如50%到70%)。
- ST22里是否还有新的
MEMORY_NO_MORE_PAGING转储出现。 - 系统整体响应时间有没有变化,是否因为Paging区变大挤压了其他内存区域而变慢。
- 结合SM50和AL08,看高峰时段的工作进程和用户数是否和调整前有变化。
如果两周内水位稳定、无新增转储,这次调整就算完成了。如果水位还是偏高,就要顺着内存分配链路继续查,这时候重点就不是PG_SHM,而是EM配置和ABAP程序本身的优化了。
5. 几个真实案例:参数不是越大越稳
5.1 案例A:PG_SHM给太大,反而把EM挤没了
某个测试系统,物理内存64GB,只跑一个SAP实例。为了彻底解决Paging爆掉的问题,同事把rdisp/PG_SHM从默认值直接提到了32GB。结果Paging区是不爆了,系统却开始频繁出现其他转储,比如TSV_TNEW_PAGE_ALLOC_FAILED,表现是EM区域反复分配失败。
原因不复杂:这台机器的物理内存一共64GB,SAP实例可用内存大概只有50GB左右,其中工作进程、共享内存、EM本来已经占掉大部分,Paging区抢走32GB后,EM的自然水位被压缩,数据全挤到Paging区,Paging区虽然不爆了,但内存访问路径变长,系统性能反而下降。
后来把这个实例的EM相关参数适当调大,同时把Paging区回调到12GB,再观察ST02,两侧水位都正常了。这个案例说明了一件事:内存调优是给不同区域分配预算,不是单个区域越大越好。
5.2 案例B:PG_MAXFS长期默认,报表一跑就爆
另一个生产系统,平时很稳定,但只要财务部跑某个固定资产报表,ST22里就出现MEMORY_NO_MORE_PAGING。排查发现,这个报表程序在取数时使用了多次嵌套循环,中间数据全部放在内表里,产生了一个特别巨大的内部上下文对象。
由于rdisp/PG_MAXFS保持默认值,单个上下文在Paging区能用的空间有限,程序一执行到某一步就触顶转储。处理方式分两层:短期把PG_MAXFS在当前基础上适当放大(同时确认它没有超过PG_SHM的20%),程序不再报错;长期把报表的ABAP代码优化任务提给开发团队,从数据取数方式上降低内存消耗。
这类案例很典型:Paging区总量够,但单用户上限卡住了程序。如果你只调大PG_SHM,不会解决问题,因为卡住的地方在PG_MAXFS。
5.3 案例C:物理内存没满,但Paging区满了
还有一次,系统物理内存还剩下30%左右,但ST02里Paging区域长时间在95%以上。很多同事会怀疑监控有问题,其实原因在EM。
这系统的ztta/roll_extension配置得明显偏小,可用EM只有1GB左右。并发用户一多,EM瞬间见底,所有用户上下文全部被挤到Paging区。Paging区承担了本该由EM承担的数据量,自然一下子就满了。
这个案例的解决方案是扩大EM的容量,而不是继续加Paging区。把ztta/roll_extension从1GB调整到4GB后,用户上下文大部分留在了EM里,Paging区的水位自己就降下来了。这也再次说明了那句老话:Paging区只是安全垫,上游够大才是真的稳。
5.4 我的调优底线:反复踩坑后的几条铁律
这些年在生产环境里和MEMORY_NO_MORE_PAGING打交道,踩过不少坑,最后总结出几条底线,一直贴在自己的文档里:
- 先看ST02,再动参数。任何内存调优动作,没有水位数据作为依据,都是拍脑袋。
- 每次只动一个参数。Paging、EM、Roll三个区域相互影响,一次改多个参数,出了问题根本分不清是谁干的。
- 调大参数前先确认物理内存有余量。SAP内部Paging区也是要占物理内存的,服务器层面没有余量,内部怎么调都是空转。
- 重视ST22里的程序信息。如果错误集中在某个报表或事务代码,别忘了让ABAP开发同事看一眼程序的内存消耗逻辑,有时候优化一段代码比调十个参数都管用。
- 改完参数必须观察至少一周。不要以为改完重启就万事大吉,内存水位是波动的,只有过了业务高峰才算验证通过。
MEMORY_NO_MORE_PAGING这个转储,说穿了就是SAP内部换页区触顶。搞清楚Paging机制之后,你会发现它并不是一个特别可怕的问题:顺着ST22找触发程序,用ST02看水位,再用RZ11核对参数,按需求调整rdisp/PG_SHM和rdisp/PG_MAXFS,配合EM等其他内存区域的联动检查,基本都能解决。关键是用数据说话,而不是凭直觉加参数。
最后再分享一个小习惯:每次调整完内存参数,我会把调整前后的ST02截图、参数值、业务高峰用户数一起归档,做成一份简单的“内存变更记录”。后面系统再出类似问题,翻一下记录就能快速定位,省时省力。这个习惯帮我解决了很多次“这个参数之前是怎么调的”的尴尬,也推荐给你。
