Normally if we have a reference and we are done using it; the reference is eligible for garbage collection. However in some cases we may want a different behavior. The package "java.lang.ref" contains classes that allow us to accomplish this.
The different types of references are:
Strong References
Soft References
Weak References
Phantom References
The names and terminology make the concepts a bit confusing and it is best to look at some code to understand these concepts.
Strong References
These are the normal Java references we are used to.
File: "ref1.java"
class sample
{
int arr1[] = new int[ 100000 ] ;
protected void finalize()
{
System.out.println( "Sample object getting finalized." ) ;
}
}
public class ref5
{
public static void main( String[] args ) throws Exception
{
sample sampleObject = new sample() ;
sampleObject = null ;
System.gc() ;
}
}
Output:
C:\Java\References>java ref1
Sample object getting finalized.
We have created a class called "sample" and placed the "finalize" method in it so that we can tell when the object of this class is garbage collected. We create an instance of the class "sample" and then assign null to it . Now it is eligible for garbage collection. We then run "System.gc()" on it. This is a hint to the JVM to start the Garbage Collection. The object gets garbage collected and the finalize method is executed. The "finalize" method can cause unexpected behavior sometimes.
File: "ref2.java"
class sample
{
int arr1[] = new int[ 100000 ] ;
protected void finalize()
{
System.out.println( "Sample object getting finalized." ) ;
try
{
Thread.sleep ( 30000 ) ;
}
catch( Exception except )
{
}
}
}
//-------------------------------------------------------------
public class ref2
{
public static void main( String[] args ) throws Exception
{
while ( true )
{
new sample() ;
} //while
}
}
Output:
C:\Java\References\strong>java ref2
Sample object getting finalized.
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at sample.<init>(ref2.java:4)
at ref2.main(ref2.java:32)
In our "main" method we have an infinite loop that creates an object and then the object is ready for garbage collection and then the loop creates another object.
However the garbage collection is done by a separate thread and when it calls the "finalize" method. This method has a "sleep" method causing the Garbage Collector thread to hang and in the meantime new "sample" objects are getting created in the program thread leading to an "OutOfMemoryError" . We will see later how "Phantom References" can perform similar functionality.
Soft References
Soft references are references that are eligible for collection if there are no other strong references to it. We can still obtain the original object through it.
However the Garbage Collector will collect it in the extreme case of memory running out. The Soft Reference stays as long as it's possible in memory. If we have a strong reference then we are sure that it will not be garbage collected. Soft Reference allows us to access the original element if it is there and still allows for garbage collection.
File: "ref3.java"
import java.util.* ;
import java.lang.ref.* ;
//-------------------------------------------------------------
class sample
{
int arr1[] = new int[ 1000000 ] ;
public void method1()
{
System.out.println( "Calling method1 inside sample class." ) ;
}
protected void finalize()
{
System.out.println( "Sample object getting finalized." ) ;
}
}
//-------------------------------------------------------------
public class ref3
{
public static void main( String[] args ) throws Exception
{
sample sampleObject = new sample() ;
SoftReference<sample> softRefObject = new SoftReference<sample>( sampleObject ) ;
sampleObject = null ;
sampleObject = softRefObject.get() ;
if( sampleObject != null )
sampleObject.method1() ;
}
}
//-------------------------------------------------------------
Output:
C:\Java\References\soft>java ref3
Calling method1 inside sample class.
In the above example we create a "sample" object and then use it in the constructor for a "SoftReference" . We can then take the object our of scope by assigning it null.
sampleObject = null ;
We can do a "get" on the soft reference to check if the object is there and if it is we are calling a method on it.
Program:
import java.util.* ;
import java.lang.ref.* ;
//-------------------------------------------------------------
class sample
{
int arr1[] = new int[ 1000000 ] ;
int i1 = 0 ;
sample( int i1p )
{
i1 = i1p ;
}
public void method1()
{
System.out.println( "Calling method1 inside sample class." ) ;
}
protected void finalize()
{
System.out.println( "Sample object:" + i1 + " getting finalized." ) ;
}
}
//-------------------------------------------------------------
public class ref3
{
public static void main( String[] args ) throws Exception
{
sample sampleObject = new sample( 1 ) ;
SoftReference<sample> softRefObject = new SoftReference<sample>( sampleObject ) ;
sampleObject = null ;
sampleObject = softRefObject.get() ;
if( sampleObject != null )
sampleObject.method1() ;
sampleObject = null ;
System.gc() ;
}
}
//-------------------------------------------------------------
Output:
C:\Java\References\soft>java ref3
Calling method1 inside sample class.
In the above we ran the "System.gc()" to show that the soft reference is not cleared by the garbage collector.
Imagine a cache that can grow. We need to be careful it doesn't exceed the memory. We can place Soft References inside the cache instead of normal references. If the memory is running low then the Garbage Collector can remove the objects. If we get a null upon retrieval then we need to place the original object back int he cache.
File: "ref4.java"
import java.util.* ;
import java.lang.ref.* ;
//-------------------------------------------------------------
class sample
{
int arr1[] = new int[ 1000000 ] ;
int i1 = 0 ;
sample( int i1p )
{
i1 = i1p ;
}
public void method1()
{
System.out.println( "Calling method1 inside sample class." ) ;
}
protected void finalize()
{
System.out.println( "Sample object:" + i1 + " getting finalized." ) ;
}
}
//-------------------------------------------------------------
public class ref4
{
public static void main( String[] args ) throws Exception
{
Vector< SoftReference > cache = new Vector< SoftReference > () ;
int index = 1 ;
while ( true )
{
sample sampleObject = new sample( index++ ) ;
cache.add ( new SoftReference<sample>( sampleObject ) ) ;
} //while
}
}
//-------------------------------------------------------------
Output:
C:\Java\References\soft>java ref4
Exception in thread "main" Sample object:912 getting finalized.
Sample object:385 getting finalized.
java.lang.OutOfMemoryError: Java heap space
Sample object:1727 getting finalized.
at sample.<init>(ref2.java:9)Sample object:0 getting finalized.
at ref2.main(ref2.java:40)Sample object:1726 getting finalized.
Sample object:1725 getting finalized.
In the above we are placing a load on the memory by continuously creating objects and adding them as SoftReference a Vector.
while ( true )
{
sample sampleObject = new sample( index++ ) ;
cache.add ( new SoftReference<sample>( sampleObject ) ) ;
} //while
This will result in the cache increasing in size and at some point JVM will start to release the memory for the added objects. Note that in the above case the program still crashed as the "while" loop kept creating objects. Also note that the
strong reference "sampleObject" went out of scope at the end of the while loop.
Weak References
Weak references are similar to Strong references with the difference that the weak reference is immediately eligible for garbage collection .
Program:
import java.util.* ;
import java.lang.ref.* ;
//-------------------------------------------------------------
class sample
{
int arr1[] = new int[ 1000000 ] ;
public void method1()
{
System.out.println( "Calling method1 inside sample class." ) ;
}
protected void finalize()
{
System.out.println( "Sample object getting finalized." ) ;
}
}
//-------------------------------------------------------------
public class ref1
{
public static void main( String[] args ) throws Exception
{
sample sampleObject = new sample() ;
WeakReference<sample> softRefObject = new WeakReference<sample>( sampleObject ) ;
sampleObject = null ;
sampleObject = softRefObject.get() ;
if( sampleObject != null )
sampleObject.method1() ;
sampleObject = null ;
//Let's run the gc
System.gc() ;
}
}
//-------------------------------------------------------------
Output:
C:\Java\References\weak>java ref1
Calling method1 inside sample class.
Sample object getting finalized.
We can see in the above program that even if the object is null we can still retrieve it from the WeakReference and if we set the strong reference "sampleObject" to null then the garbage collector picks it up right away.
Weak references can be used in "WeakHashMap" class where the keys are weak references. If there are no strong references then the Garbage Collector will remove all the unreachable keys from it.