1. 程式人生 > 其它 >Java 和 ABAP 中的幾種引用型別的分析和比較

Java 和 ABAP 中的幾種引用型別的分析和比較

Java 程式語言中幾種不同的引用型別是面試時經常容易被問到的問題:強引用,軟引用,弱引用,虛引用。

其實除了 Java 之外,某些 其他程式語言也有類似概念,比如 ABAP。今天我們就來比較一下。

根據 ABAP 幫助文件,我們可以把某個物件的引用包在一個 Weak Reference 的例項裡。ABAP 的 Weak Reference 例項通過類 CL_ABAP_WEAK_REFERENCE 實現。

看下面的例子:首先我在堆上建立了一個新的 LCL_PERSON 例項,然後包到一個 ABAP weak reference 裡。

lo_person = NEW lcl_person( 'Jerry' ).

lo_weak = NEW cl_abap_weak_reference( lo_person ).

稍後,我們想拿到被包裹的 lo_person 引用時,使用 weak reference 提供的 get 方法。見下圖示例:

lo_person = CAST lcl_person( lo_weak->get( ) ).

引用 lo_person 什麼時候會變成 initial 呢?如果當 ABAP 垃圾回收器(Garbage Collector)開始工作時,已經沒有任何引用再指向 lo_person, 則 lo_person 會變成 initial。

看下面這個例子加深理解。

REPORT ztest.
PARAMETERS: clear TYPE char1 as CHECKBOX DEFAULT abap_true,
gc TYPE char1 as CHECKBOX DEFAULT abap_true.
CLASS lcl_person DEFINITION.
PUBLIC SECTION.
DATA: mv_name TYPE string.
METHODS: constructor IMPORTING !iv_name TYPE string.
ENDCLASS.
CLASS lcl_person IMPLEMENTATION.
METHOD: constructor.

me->mv_name = iv_name.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA: lo_person TYPE REF TO lcl_person,
lo_weak TYPE REF TO cl_abap_weak_reference.
lo_person = NEW lcl_person( 'Jerry' ).
lo_weak = NEW cl_abap_weak_reference( lo_person ).
IF clear = abap_true.
CLEAR: lo_person.
ENDIF.
IF gc = abap_true.
cl_abap_memory_utilities=>do_garbage_collection( ).
ENDIF.
lo_person = CAST lcl_person( lo_weak->get( ) ).
IF lo_person IS INITIAL.
WRITE: / 'reference not available'.
ELSE.
WRITE: / 'reference still available'.
ENDIF.

這個 report 有兩個開關,如下圖。第一個開關控制 lo_person 這個引用是否被關鍵字 CLEAR 顯式地置為 INITIAL, 第二個開關決定是否在程式碼中顯式地呼叫 ABAP 垃圾回收器。

這兩個開關的開啟和關閉狀態,一共有 4 種組合。

在第一種情況下,通過關鍵字 CLEAR 清除了 lo_person 的引用,從 ABAP 的記憶體檢查器(事務碼 s_memory_inspector)能發現,lo_person 現在已經不指向任何記憶體中的物件了。

對於其他三種情況,LCL_PERSON 的例項都不會被 ABAP 垃圾回收器清除:

Java

Java 中的 weak reference 表現行為和 ABAP 一致。

我把上面的 ABAP 測試程式碼用 Java 程式重新寫一遍:

import java.lang.ref.WeakReference;
class Person {
private String mName;
public Person(String name) {
this.mName = name;
}
public String getName() {
return this.mName;
}
}
public class WeakReferenceTest {
public static void check(Person person) {
if (person == null) {
System.out.println("Reference invalid");
}
else {
System.out.println("Reference still available");
}
}
public static void main(String[] args) {
Person jerry = null;
WeakReference<Person> person = new WeakReference<Person>(new Person( "Jerry"));
jerry = new Person("Ben");
// if you comment out this line, Reference will be available
System.gc();
Person restore = person.get();
check(restore);
}
}

ABAP Soft reference - ABAP 軟應用

在我目前使用的 ABAP Netweaver 750 SP4 系統中,ABAP 軟應用尚未實現,

在系統裡只有個空的 CL_ABAP_SOFT_REFERENCE, 其描述資訊寫的是 Do Not Use this Class!

那麼我們就來試試 Java 的軟應用 Soft Reference:

package reference;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
class Person2 {
private String mName;
public Person2(String name) {
this.mName = name;
}
public String getName() {
return this.mName;
}
public void finalize() {
System.out.println("finalize called: " + this.mName);
}
public String toString() {
return "Hello, I am " + this.mName;
}
}
public class SoftReferenceTest {
public static void main(String[] args) {
SoftReference<Person2> person = new SoftReference<Person2>(new Person2( "Jerry"));
System.out.println(person.get());
ArrayList<Person2> big = new ArrayList<Person2>();
for (int i = 0; i < 10000; i++) {
big.add(new Person2(String.valueOf(i)));
}
System.gc();
System.out.println("End: " + person.get());
}
}

控制檯打印出的輸出:

Hello, I am Jerry

End: Hello, I am Jerry

即便我建立了 1 萬個 Person 物件的例項,確實消耗了一些記憶體,然後記憶體消耗還遠遠沒有大到會導致包含在軟應用中的 Person2 類的引用被 JDK 刪除掉的程度。因此我在程式碼中呼叫 Java 的垃圾回收器 System.gc()之後,該引用仍然存在。

Java培訓中,軟應用通常被用來實現在記憶體資源很有限的環境下的快取機制,比如 Android 手機開發中。

Java 虛引用 PhantomReference

使用下面的程式碼測試虛引用:

package aop;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
public class PhantomReferenceTest {
public static void main(String[] args) {
Object phantomObj;
PhantomReference phantomRef, phantomRef2;
ReferenceQueue phantomQueue;
phantomObj = new String("Phantom Reference");
phantomQueue = new ReferenceQueue();
phantomRef = new PhantomReference(phantomObj, phantomQueue);
System.out.println("1 Phantom Reference:" + phantomRef.get());
System.out.println("2 Phantom Queued: " + phantomRef.isEnqueued());
phantomObj = null;
System.gc();
System.out.println("3 Anything in Queue? : " + phantomQueue.poll());
if (!phantomRef.isEnqueued()) {
System.out.println("4 Requestion finalization.");
System.runFinalization();
}
System.out.println("5 Anything in Queue?: " + phantomRef.isEnqueued());
phantomRef2 = (PhantomReference) phantomQueue.poll();
System.out.println("6 Original PhantomReference: " + phantomRef);
System.out.println("7 PhantomReference from Queue: " + phantomRef2);
}}

測試輸出:

1. Phantom Reference: null

2. Phantom Queued: false

3. Anything in Queue? : null

5. Anything in Queue?: true

6. Original PhantomReference: java.lang.ref.PhantomReference@2a139a55

7. PhantomReference from Queue: java.lang.ref.PhantomReference@2a139a55

和之前介紹的弱引用(WeakReference)和軟引用(SoftReference)不同,包裹在虛引用(PhantomReference)中的物件例項無法通過需引用的 get 方法返回,因此在第一行輸出我們會看到: “1. Phantom Reference: null”.

在上面示例程式碼中虛引用 PhantomReference 的建構函式裡, 我傳入了一個佇列作為輸入引數。當包裹在虛引用例項中的物件引用被 Java 垃圾回收器刪除時,虛引用例項本身會自動被 JVM 插入我之前指定到虛引用建構函式輸入引數的那個佇列中去。

在 System.runFinalization()執行之前,phantomRef.isEnqueued()返回 false,phantomQueue.poll()返回空。

當 phantomObj 例項被 JVM 刪除後, 虛引用 PhantomReference 本身被加入到佇列中,並且能夠通過佇列提供的 API 所訪問:phantomQueue.poll(). 列印輸出的第 6 行和第 7 行也說明了這一點。