Java和ABAP的垃圾回收机制(Garbage Collection)比较

转载地址:https://cloud.tencent.com/developer/article/1685029

Java和ABAP的垃圾回收机制(Garbage Collection)比较


Java 和 ABAP 都可以支持自动垃圾回收。 作为应用程序开发人员,GC 过程对我们来说是完全透明的,并且在我们的应用程序代码中,大多数时候我们不应该调用 Java 或 ABAP 提供的 GC API。 然而,如果我们对垃圾收集的触发点有一些基本的了解,它可以帮助我们编写更健壮的代码,并且内存消耗相对较低。

Java中的垃圾收集

使用下面这个小代码进行测试,它每 100 毫秒分配 2MB 内存。

  package memoryTest;
    
    import java.util.Timer;
    import java.util.TimerTask;
    
    class MyTask extends TimerTask{
    
    	static final int MB =  * ;
    	@Override
    	public void run() {
    		 byte[] a1 = new byte[ * MB];
             a1[] = ;
    		 Runtime runtime = Runtime.getRuntime();
             System.out.print("total :" + (runtime.totalMemory() / )+ " KB\n");
             long free = runtime.freeMemory() / ;
             System.out.print("free:" + free+ " KB\n");
    	}
    }
    
    public class MemoryAllocationTest {
    	static public void main(String[] arg){
    		    Timer timer = new Timer();
    		    timer.schedule(new MyTask(), , );
    		}
    	}

Java 中的次要垃圾收集

使用以下 VM 参数执行程序。 选项 PrintGCDetails 可以打印出 GC 执行细节,Xmx40m 表示允许的最大堆分配大小为 40MB。

在这里插入图片描述

执行时,您可以在控制台中找到以下输出:

总计:39424 KB 免费:35720 KB 总计:39424 KB 免费:33672 KB 总计:39424 KB 免费:31624 KB 总计:39424 KB 免费:29576 KB GC (分配失败) [PSYoungGen: 9847K->744K(11776K)] 9847K->752K(39424K), 0.0022270 secs 总计:39424 KB 免费:36457 KB 总计:39424 KB 免费:34409 KB 总计:39424 KB 免费:32361 KB 总计:39424 KB 免费:30313 KB GC (分配失败) [PSYoungGen: 9102K->616K(11776K)] 9110K->632K(39424K), 0.0018849 secs 总计:39424 KB 免费:36564 KB GC 日志清楚地表明,每次 JVM 在年轻代中找不到合适的内存区域时,都会自动启动一次 GC 操作。

在这里插入图片描述
我们分析一下日志:[GC(分配失败)[PSYoungGen: 9847K->744K(11776K)] 9847K->752K(39424K), 0.0022270 secs]
在这里插入图片描述

了解这个日志的一些知识

从 Java 官网上我们知道 Hotspot 中的 Heap(一种 JVM)由年轻代、老年代和永久代组成。 (提醒: Java 8 中移除了永久生成,而引入了元空间 )

年轻代是所有新对象被分配和老化的地方。 当年轻代填满时,这会导致次要垃圾收集。 在我们的日志中可以观察到这次 GC,一旦 GC 完成,年轻代的可用空间大小会大大增加。

在这里插入图片描述

所有次要垃圾收集都是“停止世界”事件。 这意味着所有应用程序线程都将停止,直到操作完成。 在 GC 上花费的时间也会打印在日志中,在我们的示例中:

在这里插入图片描述

Java 中的完整垃圾回收

现在让我们对源代码进行一些更改。 这次我分配了 1GB。

在这里插入图片描述

VM 参数相应地更改为 -Xmx6000m。

在这里插入图片描述

日志:

在这里插入图片描述

Java中的最后一次测试

现在我将选项 Xmx6000m 更改为 Xmx900m,并且按预期引发了 OutOfMemoryError 异常。 从日志中我们知道,GC 仍然尽最大努力寻找可用内存进行分配,但失败了。
在这里插入图片描述

ABAP 中的垃圾收集

执行以下程序两次,第一次使用手动 clear = abap_false,第二次使用 abap_true。

   PARAMETERS: clear TYPE c as CHECKBOX DEFAULT abap_false.
    
    TYPES: tt_table TYPE TABLE OF tadir WITH KEY pgmid object.
    DATA: lt_result TYPE TABLE OF tadir,
          lt_total  TYPE TABLE OF tadir,
          lr_result TYPE REF TO tt_table.
    
    DATA: c1 TYPE cursor.
    
    OPEN CURSOR @c1 FOR SELECT * FROM tadir.
    
    DO.
      WRITE: / |Round: { sy-index } | COLOR COL_NEGATIVE.
      CREATE DATA lr_result.
      FETCH NEXT CURSOR @c1 INTO TABLE @lr_result->* PACKAGE SIZE 800000.
      IF sy-subrc <> 0.
        EXIT.
      ENDIF.
      APPEND LINES OF lr_result->* TO lt_total.
      cl_abap_memory_utilities=>get_memory_size_of_object( EXPORTING object = lt_total
          IMPORTING
            bound_size_alloc = DATA(bound_alloc)
            bound_size_used = DATA(bound_used) ).
    
      WRITE: / 'bound alloc:' , bound_alloc.
      WRITE: / 'bound used:' , bound_used.
    
      cl_abap_memory_utilities=>get_total_used_size( IMPORTING size = DATA(lv_before_size) ).
      WRITE: / |Total size before GC: { lv_before_size }| COLOR COL_POSITIVE.
      IF clear = abap_true.
         CLEAR: lr_result->*, lt_result, lr_result.
      ENDIF.
      cl_abap_memory_utilities=>do_garbage_collection( ).
      cl_abap_memory_utilities=>get_total_used_size( IMPORTING size = DATA(lv_after_size) ).
      WRITE: / |Total size after GC: { lv_after_size }| COLOR COL_GROUP.
      DATA(rate) = ( lv_before_size - lv_after_size ) * 100 / lv_before_size.
      WRITE: / |Freed rate: { rate }%| COLOR COL_TOTAL.
    ENDDO.
    
    WRITE: / lines( lt_total ).

在这里插入图片描述

比较两次执行的输出,我们发现最终填充的结果 lt_total 对于两次执行都是相等的。 而用于存储每次迭代时从数据库表中查询到的数据的临时变量lr_result->*、lt_result和lr_result,一旦内容附加到最终结果内表后就没有用了,原则上可以手动清除。 比较表明,如果在一个循环中你有一个局部变量,它保存了大量的数据,这些数据只在当前循环中处理,那么你可以在下一轮循环开始释放内存之前手动清除它。

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vehtBAFO-1598236863158)(https://upload-images.jianshu.io/upload_images/2085791-f48a0fb39770eaa8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

您可能会争辩说,下面的行在比较过程中真的生效了吗?

cl_abap_memory_utilities=>do_garbage_collection()。

好问题! 如果它被注释掉并使用手动 clear = abap_true 执行报告,仍然会打印出与注释掉之前相同的输出。 那么让我们看另一个例子:

  REPORT z.
    
    PARAMETERS: call_gc type char1 as CHECKBOX DEFAULT abap_false.
    class lcl_memory_test DEFINITION.
      public SECTION.
           methods: constructor.
       PRIVATE SECTION.
           data: mt_data type TABLE OF tadir.
    
    endclass.
    
    class lcl_memory_test IMPLEMENTATION.
       method: constructor.
          SELECT * INTO TABLE mt_data FROM tadir.
       endmethod.
    endclass.
    
    START-OF-SELECTION.
       data(lo_class) = new lcl_memory_test( ).
    
       cl_abap_memory_utilities=>get_total_used_size( IMPORTING size = DATA(lv_before_size) ).
      WRITE: / |Total size before GC: { lv_before_size }| COLOR COL_POSITIVE.
      CLEAR: lo_class.
      IF call_gc = abap_true.
        cl_abap_memory_utilities=>do_garbage_collection( ).
      ENDIF.
      cl_abap_memory_utilities=>get_total_used_size( IMPORTING size = DATA(lv_after_size) ).
      WRITE: / |Total size after GC: { lv_after_size }| COLOR COL_GROUP.
      DATA(rate) = ( lv_before_size - lv_after_size ) * 100 / lv_before_size.
      WRITE: / |Freed rate: { rate }%| COLOR COL_TOTAL.

这次我将代码包装起来,使用开关 call_gc 手动触发 GC:

在这里插入图片描述

先用 call_gc = abap_false 执行,结果显示即使手动清除了 lo_class 的引用,仍然没有释放内存。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OoCoEmUZ-1598236863159)(https://upload-images.jianshu.io/upload_images/2085791-3f0498690e66df0a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

第二次 call_gc = true。 GC 处理器做得很好!

在这里插入图片描述

最后但同样重要的是,这个 ABAP 示例的目的不是要求您在应用程序代码中手动调用 GC API,相反,您应该始终避免这样做。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值