SAP ABAP和Java里的弱引用(WeakReference)和软引用(SoftReference)

Jerry前一篇文章 SAP ABAP一组关键字 IS BOUND, IS NOT INITIAL和IS ASSIGNED的用法辨析 介绍了在ABAP里判断引用变量是否包含了一个有效引用的关键字:IS BOUND.

本文则从ABAP和Java编程语言里不同的引用类型这个角度来继续引用这个话题的讨论。

不知道大家留意过这个ABAP抽象类CL_ABAP_REFERENCE吗?这个抽象类只有一个GET方法,返回一个对象引用。

它的两个子类CL_ABAP_SOFT_REFERENCE和CL_ABAP_WEAK_REFERENCE,分别实现了抽象类的GET方法,不过均在ABAP Kernel里实现的,对ABAP应用开发人员来说,看不见源代码,是一个黑盒子。

怎么使用这个类呢?还是查看SAP帮助文档:

An object in the system class CL_ABAP_WEAK_REFERENCE represents a weak reference to an object in a class. Unlike regular object references, a weak reference is ignored during execution of the garbage collector. This means that a weak reference does not prevent the referenced object from being deleted when the garbage collector is executed.

CL_ABAP_WEAK_REFERENCE类的实例, 代表指向一个对象实例的弱应用。从字面上理解,既然存在弱引用,自然也存在其对立面的强应用。假设有一个ABAP类lcl_person:

DATA: lo_person TYPE REF TO lcl_person.
CREATE OBJECT lo_person.

上述代码定义了一个指向lcl_person对象实例的强引用变量,名为lo_person. 当垃圾回收器工作的时候,只要lcl_person对象实例的强引用lo_person还有效(即没有调用CLEAR, 或者没有被重新赋值指向其他的对象实例), 则lo_person对象实例所占据的内存区域不会被ABAP垃圾回收器释放。换句话说,lcl_person对象实例如果至少存在一个指向它的强引用,则在任何情况下,其内存区域都不会被ABAP垃圾回收器回收。

而弱引用,在垃圾回收阶段会直接被忽略。这就意味着,在ABAP垃圾回收器开始工作的时候,如果一个对象实例并未有任何强引用指向它,此时无论有无弱引用指向它,该对象实例都无法逃脱被回收的命运。

看个具体的例子。

这个30行的ABAP报表,实现了一个简单的LCL_PERSON类。第17行创建了一个该类的实例,该实例的强引用存储在引用变量lo_person里。

第18行创建了一个包裹LCL_PERSON对象实例的弱引用lo_weak. 调用弱引用lo_weak的get方法,在两种不同的情况下有两种不同的返回结果:

(1) 如果第17行创建的lcl_person对象实例已经被垃圾回收器回收了,则get返回一个空引用;
(2) 如果lcl_person对象实例没有被回收,则返回指向其的引用。

需要本文源代码的朋友,可以在我的SAP社区博客里找到:

Weak reference in ABAP and Java

我给这个ABAP程序指定了两个输入参数,clear和gc,分别控制是否清除强引用变量lo_person,和是否用代码调用ABAP垃圾回收器。

如果执行程序时传入的参数clear置为true,则调用CLEAR: lo_person. 根据ABAP帮助文档,CLEAR施加在引用变量lo_person上,执行后lo_person指向空引用(null reference).

另一个参数gc置为true,则用代码的方式启动ABAP垃圾回收器:cl_abap_memory_utilities=>do_garbage_collection.

这两个开关的开闭情况,构成了4种不同的排列组合。在这四种排列组合下,由弱引用lo_weak指向的对象实例,是否会被ABAP垃圾回收器回收?结果如下表:

由此可见,弱引用指向的对象实例,在ABAP垃圾回收器启动之后,如果没有再被至少一个强引用变量所指向,则会被垃圾回收器回收。

使用事务码s_memory_inspector,在垃圾回收器启动之后制作一个内存快照(Memory Snapshot),发现在上表第一种排列组合下,lcl_person对象实例已经被回收了: No memory objects found.

而其他三种排列组合下,lcl_person都逃脱了被垃圾回收器回收的命运:

Java里也有对应CL_ABAP_WEAK_REFERENCE的弱引用实现:java.lang.ref.WeakReference.

Jerry本文的ABAP程序,翻译成Java代码如下:

因为这两种编程语言的弱引用,工作原理完全一致,所以上面Java版本的例子Jerry就不赘述了。

上面第25行代码里将强引用变量jerry指向的对象置为null,第26行启动Java垃圾回收器,于是第27行调用弱引用变量get方法得到的结果是:null.

那么这种弱引用有什么使用场景?

最好的学习方式就是对CL_ABAP_WEAK_REFERENCE执行Where-used操作,来查找有哪些SAP标准应用使用到了这个类。

在下图Jerry使用的SAP CRM系统里,弱引用的使用场合还不少。

这500多处使用场景里,最典型的就是缓存(Cache)的实现场景。下图这个CRM增强工具Application Enhancement Tool(简称AET)工厂类的方法GGET_DATA_TYPE_HANDLER, 根据两个输入参数,字段数据类型和字段行为类型,返回对应的处理器实例(handler). 这些处理器实例化时需要从若干张数据库表里读取数据并保存在内存里,因此初始化过程需要花费一定的时间。为了避免这个方法每次被调用时都花费时间重复地访问数据库表,创建新的实例,该工厂类引入了一个缓存机制, 即下图第21行的内表gt_type_handler_cache. 每次GET方法被调用时,先去该内表里查看是否存在对应的处理器实例。如果有,直接返回,省去了费时的处理器实例化工作。

看这个缓存的设计,行项目的类型结构里,handler的类型并不是具体的IF_AXT_DATATYPE_HANDLER, 而是一个弱引用。

技术上说,上图第七行改成:

handler TYPE REF TO IF_AXT_DATATYPE_HANDLER,也是一种正确的设计,而且也正是绝大多数ABAP应用人员最常规的缓存设计方案。为讨论方便,我将这种大家都常用的方案称为方案B,而上图AET工厂类采用弱应用指向处理器实例的方案称为方案A.

方案A的优点是,进可攻退可守。

进可攻,即如果ABAP垃圾回收器没有调用,并且至少存在一个指向某处理器实例的强引用,此时两种方案运行时没有大的差异,唯一的细微区别之处就是方案A在读缓存内表命中,拿到buffer里存放的弱引用之后,再调用弱引用的get方法,拿到处理器实例并返回。而方案B读缓存内表命中后,buffer里存在的就是处理器实例本身,直接返回给调用端即可。

退可守,就是一旦程序里再也没有指向该处理器实例的强引用,并且ABAP垃圾回收器开始工作,那么弱引用指向的处理器实例会被销毁,释放了其消耗的内存。下次如果GET方法再次调用,会从数据库里重新加载数据,初始化处理器实例(下图红色区域), 并重新创建弱引用(下图蓝色区域)。

一言以蔽之,弱引用CL_ABAP_WEAK_REFERENCE最适合用于描述有一定用处,但不是必需驻留在内存里的对象实例。因此在SAP CRM很多框架代码的缓存设计上有着广泛的应用。

其实ABAP除了强引用和弱引用之外,还存在第三种类型的引用:软引用(CL_ABAP_SOFT_REFERENCE).

同弱引用相比,软引用指向的对象,只有当没有被任何强引用指向,且垃圾回收器运行时,系统内存不足时才会被销毁。系统可用内存降低到百分之多少才算是“不足”呢?软引用并未在ABAP里实现,所以我们也无法继续讨论下去。

Java里除了弱引用和软引用之外,还存在PhantomReference(虚引用).

顾名思义,Java里的虚引用就是"形同虚设",因为通过虚引用的get方法,获取到的结果永远为null.

在有的中文资料里,PhantomReference因其这种表现行为,又被翻译成"幻引用",“幽灵引用”。这个名字让我想起了《星际争霸》里人族的幽灵战机Wraith.

虚引用主要用来跟踪对象实例被垃圾回收器回收的活动,必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现还有虚引用指向这个对象实例,就会在回收该实例的内存之前,把这个虚引用加入到与之关联的引用队列中。

因为ABAP里根本没有虚引用,所以Jerry也不展开叙述了。

希望本文能让大家对ABAP里两种引用:强引用和弱引用的设计和作用有一个全面了解,同时能知道像Java这种编程语言里,还存在另外两种引用:软引用和虚引用。感谢阅读。

要获取更多Jerry的原创文章,请关注公众号"汪子熙":

展开阅读全文
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值