共享对象

共享对象 (★★)

#shared-objects #memory-mgmt

概念与动机

共享对象提供了一种在同一应用服务器上跨用户会话和外部会话共享数据的机制。与 ABAP 内存(仅限外部会话)或 SAP 内存(仅限平坦字段)不同,共享对象可以存储所有用户都可访问的复杂深层数据结构。

内存模型层次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
应用服务器
┌─────────────────────────────────────────────────────────────────────┐
│ │
│ 用户会话 A 用户会话 B │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ 外部会话 1 │ │ 外部会话 1 │ │
│ │ ┌──────────────────┐ │ │ ┌──────────────────┐ │ │
│ │ │ 内部会话 │ │ │ │ 内部会话 │ │ │
│ │ │ + ABAP 内存 │ │ │ │ + ABAP 内存 │ │ │
│ │ └──────────────────┘ │ │ └──────────────────┘ │ │
│ │ SAP 内存 │ │ SAP 内存 │ │
│ └──────────────────────┘ └──────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ 共享内存(应用服务器级别) │ │
│ │ ┌────────────────────────────────────────────────────────┐ │ │
│ │ │ 共享对象内存 │ │ │
│ │ │ ┌──────────────┐ ┌──────────────┐ ┌─────────────┐ │ │ │
│ │ │ │ 区域 1 │ │ 区域 2 │ │ 区域 N │ │ │ │
│ │ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │ │ │ │ │
│ │ │ │ │ 根对象 │ │ │ │ 根对象 │ │ │ │ │ │ │
│ │ │ │ └──────────┘ │ │ └──────────┘ │ │ │ │ │ │
│ │ │ └──────────────┘ └──────────────┘ └─────────────┘ │ │ │
│ │ └────────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘

与其他内存机制的比较

特性 ABAP 内存 SAP 内存 共享对象
范围 一个外部会话 一个用户会话 应用服务器上所有用户
数据类型 任意 仅平坦字段 任意(必须在启用共享内存的类中)
持久性 直到会话关闭 直到注销 直到显式删除或服务器重启
跨用户?
大小限制 滚动区限制 较小(SPA/GPA) 每个区域可配置

区域类

共享对象被组织为区域,每个区域由一个区域类管理。

角色 创建者
CL_SHM_AREA 所有区域类的基类 SAP(框架)
区域类(如 ZCL_SHM_MY_AREA 管理一个共享内存区域;通过事务 SHMA 生成 开发者(通过 SHMA)
区域句柄类 提供访问区域实例的句柄;与区域类一起自动生成 系统(自动生成)
数据/根类 包含作为属性的实际数据;由开发者创建 开发者

类关系

1
2
3
4
5
6
7
CL_SHM_AREA(SAP 基类)
└── ZCL_SHM_MY_AREA(区域类,由 SHMA 生成)
提供:ATTACH_FOR_WRITE, ATTACH_FOR_READ, ATTACH_FOR_UPDATE, DETACH

ZCL_MY_ROOT(数据类,由开发者创建)
包含:保存共享数据的属性
继承:无特殊要求(但必须兼容 SHM)

实现共享对象的步骤

步骤 1:定义共享内存区域(事务 SHMA)

操作 详情
事务 SHMA
区域名称 Z_SHM_CATALOG
属性 设置区域类名、根类、有效期、客户端依赖性
结果 生成区域类(如 ZCL_SHM_Z_SHM_CATALOG)和区域句柄类

步骤 2:创建根/数据类

1
2
3
4
5
6
7
8
9
10
11
CLASS zcl_catalog_root DEFINITION
PUBLIC
CREATE PUBLIC.

PUBLIC SECTION.
DATA: mt_products TYPE ztt_products, " 产品目录数据
mv_last_update TYPE timestampl.
METHODS: load_data,
get_product IMPORTING iv_id TYPE zproduct_id
RETURNING VALUE(rs_product) TYPE zs_product.
ENDCLASS.
📌根类要求
根类及其属性中使用的所有类型必须兼容 SHM。这意味着:
  • 不能引用不可 SHM 序列化的对象
  • 不能使用标记为 NOT FOR SHARED OBJECTS 的类
  • 所有引用的类型必须可在共享内存中使用

步骤 3:写入共享内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
DATA: lo_area  TYPE REF TO zcl_shm_z_shm_catalog,
lo_root TYPE REF TO zcl_catalog_root.

" 1. 附加以写入
lo_area = zcl_shm_z_shm_catalog=>attach_for_write( ).

" 2. 在共享内存中创建根实例
CREATE OBJECT lo_root AREA HANDLE lo_area.

" 3. 填充根对象数据
lo_root->load_data( ).

" 4. 设置区域的根对象
lo_area->set_root( lo_root ).

" 5. 分离(提交更改到共享内存)
lo_area->detach( ).
步骤 方法 目的
附加 ATTACH_FOR_WRITE( ) 获取区域写锁;返回区域句柄
创建根 CREATE OBJECT ... AREA HANDLE 在共享内存中实例化根类
设置根 area->set_root( root ) 注册根对象为区域的根
分离 area->detach( ) 释放锁;使更改对读者可见

步骤 4:从共享内存读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
DATA: lo_area TYPE REF TO zcl_shm_z_shm_catalog,
lo_root TYPE REF TO zcl_catalog_root.

" 1. 附加以读取
lo_area = zcl_shm_z_shm_catalog=>attach_for_read( ).

" 2. 访问根对象
lo_root = lo_area->root.

" 3. 使用数据
DATA(ls_product) = lo_root->get_product( iv_id = 'MAT01' ).

" 4. 分离
lo_area->detach( ).
步骤 方法 目的
附加 ATTACH_FOR_READ( ) 获取读锁;返回区域句柄
访问根 area->root 获取根对象的引用
分离 area->detach( ) 释放读锁

附加模式比较

模式 锁类型 使用场景 并发访问
ATTACH_FOR_READ 读锁 读取数据 允许多个读者同时访问
ATTACH_FOR_WRITE 写锁 创建或替换整个区域内容 阻塞读者和写者
ATTACH_FOR_UPDATE 写锁 修改现有内容 阻塞读者和写者
💡尽可能使用 ATTACH_FOR_READ
读锁允许多个并发读者。写锁和更新锁是排他的。设计共享对象时应以读多写少为原则,以获得最佳性能。

典型使用场景

目录(读多改少)

方面 详情
模式 一次性加载(或按计划),由多个用户读取
写入触发 首次访问或定时刷新
读取频率 非常高(每次用户请求)
示例 产品目录、配置数据、汇率
1
2
3
4
5
6
7
8
写入者(管理员/调度器):
ATTACH_FOR_WRITE → 加载数据 → 设置根 → DETACH
↓(共享内存已更新)
读取者(终端用户):
ATTACH_FOR_READ → 访问根 → 获取数据 → DETACH
ATTACH_FOR_READ → 访问根 → 获取数据 → DETACH
ATTACH_FOR_READ → 访问根 → 获取数据 → DETACH
...(多个并发读者)

购物车(跨会话数据)

方面 详情
模式 每个用户有自己的区域实例(按键区分)
写入触发 用户添加/删除项目
读取频率 每用户访问
示例 购物车、用户偏好、会话状态
⚠️共享对象仅限于单个应用服务器
共享内存是单个应用服务器本地的。如果系统有多个应用服务器,在服务器 A 上写入的数据在服务器 B 上不可见。跨服务器共享需要使用数据库表或其他持久化机制。

区域生命周期和失效

事件 行为
区域创建(首次写入) 分配内存,存储根对象
区域读取 返回引用;不复制数据
区域覆写 旧内容被替换;读者重新附加后获得新内容
区域失效 FREE_AREA 或 SHMA 事务;所有引用变为无效
服务器重启 所有共享内存被清除
1
2
" 使区域失效/释放
zcl_shm_z_shm_catalog=>free_area( ).

实现检查清单

步骤 事务/代码 完成?
1. 创建数据(根)类及其属性 SE24 / ADT [ ]
2. 在 SHMA 中定义共享内存区域 SHMA [ ]
3. 实现写入逻辑(附加 → 创建 → 设置 → 分离) 程序/方法 [ ]
4. 实现读取逻辑(附加 → 访问 → 分离) 程序/方法 [ ]
5. 处理异常(CX_SHM_* 类) Try/catch 块 [ ]

常见异常

异常 原因
CX_SHM_NO_ACTIVE_VERSION 区域尚未写入数据
CX_SHM_EXCLUSIVE_LOCK_ACTIVE 另一个进程持有写锁
CX_SHM_CHANGE attempts 尝试修改读锁保护的数据
CX_SHM_VERSION_LIMIT_EXCEEDED 区域版本过多

考试/测试模式

关键词 / 模式 正确答案
共享对象范围 同一应用服务器上的所有用户
定义区域的事务 SHMA
区域类的基类 CL_SHM_AREA
写入步骤 ATTACH_FOR_WRITE → CREATE OBJECT AREA HANDLE → set_root → DETACH
读取步骤 ATTACH_FOR_READ → 访问根 → DETACH
ATTACH_FOR_READ 锁定 允许多个读者并发
ATTACH_FOR_WRITE 锁定 排他 – 阻塞读者和写者
根类 开发者创建的类,以属性形式保存共享数据
跨服务器共享? – 共享内存是每个应用服务器独立的
使用场景:目录 读多改少 – 共享对象的理想场景
使用场景:购物车 跨会话数据,按用户创建区域实例
FREE_AREA 使共享内存区域内容失效并删除

相关笔记

  • 运行时与内存管理 – ABAP 内存、SAP 内存、会话架构
  • 程序调用 – 内部会话及其与内存的关系
  • [[_posts/SAP_MM/SCM500_采购流程/00-仪表盘/考试陷阱]] – 常见共享内存陷阱
  • [[_posts/SAP_MM/SCM500_采购流程/00-仪表盘/快速参考]] – 内存区域比较表