Recently I have some recruitment interview on the topic Weak reference and I think it is necessary for me to refresh my knowledge on this topic. When I write small program to verify my assumption, I meet with some trouble and great thanks to Horst Keller who has helped me out 🙂
According to ABAP help, we can wrap an object reference to the so called weak reference, for example see the following code:

lo_person = NEW lcl_person( 'Jerry' ).
lo_weak = NEW cl_abap_weak_reference( lo_person ).

/wp-content/uploads/2016/07/clipboard1_988005.png

And later we can get the reference to Jerry back via get method provided by weak reference.

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

The lo_person will become initial only on the conditional that there is no reference pointing to it when Garbage Collector is running. To verify it I wrote the following small program:

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.

There are two switches for this report. The first switch controls whether the reference points to Jerry will be cleared or not, the second one for the fact whether Garbage collection should explicitly be called.

/wp-content/uploads/2016/07/clipboard1_988005.png

There are four possibilities of combination, and the corresponding result are listed below:

clear reference to Jerry?

call Garbage collection?

object pointed by weak reference is cleared
by Garbage collector?

Yes

Yes

Yes

Yes

No

No

No

Yes

No

No

No

No

In first scenario, in memory snapshot ( tcode s_memory_inspector ) we can clearly see that the object pointed to by lo_person is deleted.

/wp-content/uploads/2016/07/clipboard2_988043.png

For the left three scenarios, lcl_person instance is not deleted by Garbage collector:

/wp-content/uploads/2016/07/clipboard3_988044.png

The weak reference in Java behaves the same as ABAP. You can use the following Java code to test it and get the same result:

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);
}
}

Soft reference

In my system ( Netweaver 750 SP4 ), the help says soft reference is not implemented.

/wp-content/uploads/2016/07/clipboard4_988045.png

Since it is not implemented in ABAP, we can test in Java instead.

Use the following Java code to test:

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());
}
}

This simple program will generate the following output in console:

Hello, I am Jerry
End: Hello, I am Jerry

The reason is, although I have created 10000 Person instance to consume some memory, however the memory consumption is still not so huge enough to make the Jerry person instance wrapped in Soft reference deleted. As a result after System.gc() is called, the reference is still available.

In real Java world , Soft reference is usually used to implemented cache mechanism where limited memory resource is available, for example in Android application development.

PhantomReference

There is another kind of reference in Java: PhantomReference. You can use the following code to test:

package reference;
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);
}
}

This program will generate the following output:

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

Unlike WeakReference or SoftReference,  the object wrapped in PhantomReference is never accessible, this is the reason why you see first line of output: “1. Phantom Reference: null”.

In constructor of PhantomReference, I have passed a queue as argument. When the object reference in PhantomReference is removed by Garbage collector, the PhantomReference itself ( not the deleted object reference ) will be inserted to that queue by JVM. Before System.runFinalization() is called, phantomRef.isEnqueued() returns false and phantomQueue.poll() returns nothing. After phantomObj is deleted by JVM, the PhantomReference is put to the queue and could be accessed by phantomQueue.poll(). By comparing object reference in line 6 and line 7 output, we can ensure that these two PhantomReference are exactly the same.

Further reading

I have written a series of blogs which compare the language feature among ABAP, JavaScript and Java. You can find a list of them below:
To report this post you need to login first.

1 Comment

You must be Logged on to comment or reply to a post.

Leave a Reply