【12】SAP ABAP性能优化 - 从‘lazy copy strategy‘说一说内表的使用

1. Session Memory DUMP

ABAP程序在运行过程中,每一个线程都会分配一定额度的内存,我们通常可以称之为“Session Memory”。SAP系统GUI上默认最大可以开6个窗口,一个窗口其实就是一个ABAP session。
在这里插入图片描述
Session中的程序在运行时,如果超出最大的内存限额,程序便会发生Session Memory的DUMP:
TSV_TNEW_PAGE_ALLOC_FAILED,也即分配新的内存额度失败。
在这里插入图片描述
解决这个问题的方式有两种,一种是让Basis Team调整配置文件,增加一个session中最大的内存分配;另一种就是分析代码,看看ABAP程序是否有优化的空间。

事实证明,大部分的session memory dump,其实都是由于在程序中内存使用不合理造成的。今天我们来主要分析一下由于内表使用不合理造成的内存溢出。

2. Lazy Copy Strategy (Copy-on-write)

Lazy Copy 是ABAP中的对内表分配内存时的一种策略,具体解释可看下面代码:

DATA lt_data1 TYPE TABLE OF acdoca.
DATA lt_data2 TYPE TABLE OF acdoca. 
DATA ls_data  TYPE acdoca.

lt_data2 = lt_data1. " no copy occurred, they use same memory object

APPEND ls_data TO lt_data2. " lt_data2 is changed, a new memory object is create, now the memory consumption is double

当仅仅是一个内表的变量指向指向另一个内表变量时 lt_data2 = lt_data1 ,其实并不会增加任何内存的消耗,因为它们会共用一个内存对象。

但当lt_data2中的内容发生改变时,系统会立即开辟一个新的内存对象给到它。在这个例子中,内存的消耗也变会翻番。

因此,在使用内表时,我们要避免使用多余的变量,尽可能保证一个内表仅有一个内存对象,当内表使用完毕后,也尽量清空,把内存释放掉。

3. 举个例子

我现在内表 lt_acdoca1 中准备了102400条数据,然后将 lt_acdoca1 赋值给 lt_acdoca2 和 lt_acdoca3.
我们可以看到,虽然是3个独立的变量,但它们此时存储的数据是一样的。因此它们也是共用同一个内存对象,此对象消耗了792576144 Byte的内存,也即755MB。
在这里插入图片描述
此时,当我们改变内表 lt_acdoca1 的值时,可以看到一个新的内存对象被创建出来,此时当前线程的内存中这两个变量的内存消耗为 755 + 755,也即仅1.5GB。 翻番了!
在这里插入图片描述
明白了么?对于内表使用时,特别是内表中存储的数据量非常大时,要避免多个变量同时都在存储类似(相同)的数据,因为这种无意义的行为,可能会直接消耗掉session中的所有内存。

4. Session Memory的trace工具

ABAP中的debuger中,内置了session memory的分析工具,示例见下图。当然,也可以单独使用transaction:S_MEMORY_INSPECTOR 来进行trace。有关具体的trace方式,可以参考此文档
在这里插入图片描述

5. 示例分析程序

下面给出了我在本文中,为说明问题所写的一个小程序,感兴趣的同学可以自己debug下。

CLASS lcl_acdoca DEFINITION FINAL CREATE PRIVATE.
  PUBLIC SECTION.
    TYPES tt_acdoca TYPE TABLE OF acdoca.

    CLASS-METHODS create
      RETURNING VALUE(ro_instance) TYPE REF TO lcl_acdoca.
    METHODS set_value
      IMPORTING VALUE(it_acdoca) TYPE tt_acdoca.
    METHODS set_default_company_code
      IMPORTING iv_bukrs TYPE bukrs.
  PRIVATE SECTION.
    DATA mt_acdoca TYPE tt_acdoca.
ENDCLASS.
CLASS lcl_acdoca IMPLEMENTATION.
  METHOD create.
    ro_instance = NEW #( ).
  ENDMETHOD.
  METHOD set_value.
    mt_acdoca = it_acdoca.
  ENDMETHOD.

  METHOD set_default_company_code.
    DATA(lt_acdoca) = mt_acdoca.

    LOOP AT mt_acdoca ASSIGNING FIELD-SYMBOL(<ls_acdoca>).
      <ls_acdoca>-rbukrs = iv_bukrs.
    ENDLOOP.
  ENDMETHOD.
ENDCLASS.
**********************************************************************
* Value semantic
**********************************************************************
START-OF-SELECTION.

  DATA lt_acdoca1 TYPE TABLE OF acdoca.
  DATA lt_acdoca2 TYPE TABLE OF acdoca.
  DATA lt_acdoca3 TYPE TABLE OF acdoca.

  SELECT * FROM acdoca INTO TABLE lt_acdoca1 UP TO 100 ROWS.

  LOOP AT lt_acdoca1 REFERENCE INTO DATA(lr_acdoca1).
    lr_acdoca1->gjahr = 2020.
  ENDLOOP.

  DO 10 TIMES.
    APPEND LINES OF lt_acdoca1 TO lt_acdoca1.
  ENDDO.

* Value reference
  lt_acdoca2 = lt_acdoca1.
  lt_acdoca3 = lt_acdoca1.

* changing
* lt_acdoca2[ 1 ]-gjahr = 2021.
  lt_acdoca1[ 1 ]-gjahr = 1900.
  lt_acdoca3[ 1 ]-gjahr = 2022.
**********************************************************************
* Reference semantic
**********************************************************************
  DATA lo_acdoca1 TYPE REF TO lcl_acdoca.
  DATA lo_acdoca2 TYPE REF TO lcl_acdoca.
  DATA lo_acdoca3 TYPE REF TO lcl_acdoca.

  lo_acdoca1 = lcl_acdoca=>create( ).
  lo_acdoca1->set_value( lt_acdoca1 ).
  CLEAR lt_acdoca1.
  lo_acdoca1->set_default_company_code( 'CFP1' ).

  lo_acdoca2 = lo_acdoca1.
  lo_acdoca3 = lo_acdoca1.

  lo_acdoca3->set_default_company_code( 'CFP2' ).

  cl_demo_output=>display( 'done').

本博客专注于技术分享,干货满满,持续更新。
欢迎关注❤️、点赞👍、转发📣!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十年铸器

给作者赏杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值