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行也說明了這一點。
要擷取更多Jerry的原創技術文章,請關注公衆号"汪子熙"或者掃描下面二維碼: