wood burning stoves 2.0*
The moose likes Performance and the fly likes a simple WeakReference and SoftReference exemple Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of Android Security Essentials Live Lessons this week in the Android forum!
JavaRanch » Java Forums » Java » Performance
Bookmark "a simple WeakReference and SoftReference exemple" Watch "a simple WeakReference and SoftReference exemple" New topic
Author

a simple WeakReference and SoftReference exemple

Bogdan Degeratu
Greenhorn

Joined: Jun 09, 2010
Posts: 4
I was looking for a good code sample last week that could elucidate some questions I had about the java.lang.ref api and I couldn't find one.
So I decided to write one of my own and share it here.
This example is emphasizing the WeakReference and SoftReference behaviour. Before looking at the code make sure you know what is the difference
between WeakReference and SoftReference.


Sources are included in the .jar

Scenario
Suppose we have a continuous notification flow containing asset-fluctuation information on one market.
We receive this flow on a ServerBroker that is distributing this information to each of its subscribers (in our case 2 subscribers :
Front and MiddleOfficeSubscriber). FluctuationPublisher opens a socket, publish a random Fluctuation then close the socket, every x milliseconds.

WeakReference
Lets say that ServerBroker wants to track received Fluctuation objects only the time these objects are being processed by subscribers than just
get rid of them.
ServerBroker : When receiving a Fluctuation object, ServerBroker puts <fluctuation, more information> into a WeakHashMap, executes the
multithreaded-subscribers and finally frees all strong references to the Fluctuation object before calling gc.
Subscriber : does some work with the Fluctuation object then frees all strong references to the Fluctuation object before calling gc.

Conclusion :Once subscribers execution ends all strong references to Fluctuation object are freed so gc should garbage collect the the weak referenced
fluctuation-objects along with its entry in the map. So the size of the WeakHashMap must not dramaticly increase with time even if no explicit .remove() is called
on the map and we continuously add objects into it.

Test : let the example execute for 20-30 seconds. Observe the size of the WeakHashMap. You will see that the size of the weakHashMap doesn't
increase with time.

SoftReference
FrontOfficeSubscriber is continuously computing the assets quotation on the market each time a fluctuation object is received.
Lets suppose that FrontOfficeSubscriber has severe time-computation constraints.
So you will probably want to avoid any latencies due to I/Os. You will probably want to somehow cache quotations.
SoftReference are a way of caching information. This example defines a SoftHashMap implementation (based on Heinz Kabutz exemple) that
stores the updated quotations values for all assets.
When receiving a fluctuation the FrontOfficeSubscriber first search into the SoftHashMap the quotation of the corresponding asset.
If it finds it, it computes the new quotation and store it back into the map. If not it will look for it into the database (actually
there is no database, the getAssetValueFromDatabase returns default value 10 for all assets)

Now in order to see some gc action on the SoftRefecences we have to run out of memory. Have a look at our execution parameters -Xms15m -Xmx15m -XX:NewRatio=2.
So tenured is about 10M large and young is about 5. In the Frontal subscriber if this condition is valid (Math.random() > 0.8d) we allocate ~10M of memory
block = new byte[10*1024*1024 + 500000];
This will probably go directly in the tenured (normally memory allocation is done in young, but 10M will never be available in young). Our soft referenced
objects in the map were probably already promoted in the tenured too (long living objects). So during next tenured gc we could (not necessarily) see the
size of the softHashMap reducing.

Conclution : In this example 20 types of assets are available. The size of the softHashMap is staticlly initialized with 20 default quotations.
The size of softHashMap will probably remain constant (20 quotations) untill (Math.random() > 0.8d) is true and 10M memory
allocation will happen. When so, softHashMap' size will probably decreese, soft-references beeing collected due to short memory issues.

Test : Let the example execute 20-30 seconds. Observe in the output the evolution of softHashMap' size. check out what happens when 10M is allocated (line
starts with "IMPORTANT ...").
Notice that not all the time soft referenced objects are collected. Each GC implementation has its own algorithm to decide whether to garbage-collect
or not a soft reachable object.


Rename TestReference.txt into TestReference.jar
Jar has to be executed with this commande line :
bdegerat@btx47791:~/Bureau$ java -jar -Xms15m -Xmx15m -XX:NewRatio=2 -XX:+PrintGCDetails yourjar.jar [port] [publisherFrequency]
or if you want to redirect output into a log file on unix systems
bdegerat@btx47791:~/Bureau$ java -jar -Xms15m -Xmx15m -XX:NewRatio=2 -XX:+PrintGCDetails yourjar.jar [port] [publisherFrequency] > log.txt

- port is an int value representing the BrokerServer listenning port. Default value 1234.
- publisherFrequency is a long value representing the number of milliseconds between 2 publishing. Default value is 300. Be careful when playing with this
variable. You can cause OutOfMemory Exceptions if value is too small or you can no longer see garbage-collection happening on SoftReferences if value
is too large.

jdk > jdk5. I tested with jdk6.

Enjoy



Bogdan Degeratu
Greenhorn

Joined: Jun 09, 2010
Posts: 4
Apperently I cannot upload the .jar Hereby the code (package fr.bytl.test.reference):

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

package fr.bytl.test.reference;

import java.io.Serializable;
import java.util.Random;

/**
*
* @author bdegerat
*/
public class Fluctuation implements Serializable{

public enum Asset {MICROSOFT,
ORACLE,
BARCLAYS,
BOUYGUES,
ORANGE,
VOODAFONE,
RBS,
VALEO,
GOOGLE,
SFR,
NIKE,
ADIDAS,
MOTOROLA,
SIEMENS,
APPEL,
NOKIA,
INTEL,
DELL,
TOCHIBA,
AMD}


protected String additionalInformation;
protected Asset asset;
protected double fluctuation;


public Fluctuation(String additionalInformation, Asset asset, double fluctuation) {
this.additionalInformation = additionalInformation;
this.asset = asset;
this.fluctuation = fluctuation;
}

public Fluctuation() {
}

public String getDescription() {
return additionalInformation;
}

public void setDescription(String description) {
this.additionalInformation = description;
}

public Asset getAsset() {
return asset;
}

public void setAsset(Asset asset) {
this.asset = asset;
}

public double getFluctuation() {
return fluctuation;
}

public void setFluctuation(double fluctuation) {
this.fluctuation = fluctuation;
}


public static Fluctuation generateRandomFluctuation(){
Fluctuation fluctuation = new Fluctuation();
fluctuation.setAsset(rand.random());
fluctuation.setFluctuation(randomValue(-5, 5));

return fluctuation;
}

private static final RandomEnum<Asset> rand = new RandomEnum<Asset>(Asset.class);

private static class RandomEnum<E extends Enum> {

private static final Random RND = new Random();
private final E[] values;

public RandomEnum(Class<E> token) {
values = token.getEnumConstants();
}

public E random() {
return values[RND.nextInt(values.length)];
}
}

public static double randomValue(int low, int high) {
Random rnd = new Random();
return Math.min(low, high) + rnd.nextInt(Math.abs(high - low)) + Math.random();
}
}





/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

package fr.bytl.test.reference;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
*
* @author bdegerat
*/
public class FluctuationPublisher implements Runnable {

private static int nbSentObjects = 0;
private final int port;

public FluctuationPublisher(int port) {
this.port = port;
}

public void run() {
ObjectOutputStream ostream = null ;
try {
Socket socket = new Socket(Main.HOST, port);
ostream = new ObjectOutputStream(socket.getOutputStream());
Fluctuation fluctuation = Fluctuation.generateRandomFluctuation();
fluctuation.setDescription("Fluctuation-" + getNbSentObjects());
ostream.writeObject(fluctuation);
ostream.flush();
System.out.println(fluctuation.getDescription() + " published. " + fluctuation.getAsset() + " took " + fluctuation.getFluctuation() + "%");
fluctuation = null;
ostream.close();
} catch (IOException ex) {
Logger.getLogger(FluctuationPublisher.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
ostream.close();
} catch (IOException ex) {
Logger.getLogger(FluctuationPublisher.class.getName()).log(Level.SEVERE, null, ex);
}
}
}

//The class is the monitor
public synchronized static int getNbSentObjects(){
return ++nbSentObjects;
}

}



/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

package fr.bytl.test.reference;

import fr.bytl.test.reference.Fluctuation.Asset;

/**
*
* @author bdegerat
*/
public class FrontOfficeSubscriber extends Subscriber {

private static final SoftHashMap quotations = new SoftHashMap();

static {
for(Asset asset : Asset.values()){
quotations.put(asset, new Double(10));
}
}

public FrontOfficeSubscriber(Fluctuation fluctuation, String subscriberName) {
super(fluctuation, subscriberName);
}


@Override
void doYourThing() {
updateQuotations();

}

public void updateQuotations() {
byte[] block;
Double assetCotation = null;
// this is a random 10M allocation. when this happens softReferences should be garbaged and quotations should be re-initialized with the default values.
System.out.println(subscriberName + " :Check for SofHashMap size "+ quotations.size());
if(Math.random() > 0.8d){
//System.out.println("Important memory allocation. We may have the chance to see some garbage-collecting on soft references.");
block = new byte[10*1024*1024 + 500000];
System.gc();
System.out.println("IMPORTANT " + subscriberName + ": Now memory is short, hoping gc executed. Check for SofHashMap size "+ quotations.size());
}

Object cachedObject = quotations.get(fluctuation.getAsset());
synchronized(quotations){
if(cachedObject != null){
assetCotation = (Double)cachedObject;
} else {
assetCotation = getAssetValueFromDatabase(fluctuation.getAsset());
}
assetCotation = assetCotation + fluctuation.getFluctuation()*assetCotation/100;
quotations.put(fluctuation.getAsset(), assetCotation);
//we should also update database
}
System.out.println("New cotation for " + fluctuation.getAsset() + ": " + assetCotation);
}

private Double getAssetValueFromDatabase(Asset asset) {
//no dynamic values. When softReferences garbaged we should see default cotations reapiaring
return 10d;
}

}




/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

package fr.bytl.test.reference;

import java.io.IOException;
import java.lang.ref.WeakReference;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
*
* @author bdegerat
*/
public class Main {

public static final int DEFAULT_PORT = 1234;
public static final int DEFAULT_SLEEP_TIME = 1234;
public static final String HOST = "localhost";

/**
* @param args the command line arguments
*/
public static void main(String[] args) throws Exception {
int port = DEFAULT_PORT;
long publisherSleepTime = DEFAULT_SLEEP_TIME;
if(args != null && args.length > 0) {
port = new Integer(args[0]).intValue();
if(args != null && args.length > 1){
publisherSleepTime = new Long(args[1]).longValue();
}
}
startServer(port);
System.out.println("Server started on " + HOST + ":" + port);
startPublishers(publisherSleepTime, port);



}

public static void startServer(int port) throws IOException{
final ServerSocket weakReferenceServer = new ServerSocket(port);
new Thread() {
public void run() {
try {
while(true) {
Socket newSocket = weakReferenceServer.accept();
Thread serverThread = new Thread(new ServerBroker(newSocket));
serverThread.start();
}
} catch (IOException ex){
ex.printStackTrace();
}
}
}.start();
}

public static void startPublishers(long publisherSleepTime, int port) {
while(true){
Thread thread = new Thread(new FluctuationPublisher(port));
thread.start();
try {
Thread.sleep(publisherSleepTime);
} catch (InterruptedException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
}

}






/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

package fr.bytl.test.reference;

import java.util.logging.Level;
import java.util.logging.Logger;

/**
*
* @author bdegerat
*/
public class MiddleOfficeSubscriber extends Subscriber {

public MiddleOfficeSubscriber(Fluctuation fluctuation, String subscriberName) {
super(fluctuation, subscriberName);
}



@Override
void doYourThing() {
try {
//simulate some time-processing before returning
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(MiddleOfficeSubscriber.class.getName()).log(Level.SEVERE, null, ex);
}
}

}







/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

package fr.bytl.test.reference;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.Socket;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
*
* @author bdegerat
*/
public class ServerBroker implements Runnable{

protected static Map<Fluctuation, String> currentlyProcessedFluctuations = new WeakHashMap<Fluctuation, String>();
private Socket socket;

public ServerBroker(Socket socket) throws IOException {
this.socket = socket;
}

public void run() {
// istream sometimes keeps strong reference to fluctuation (caches read-objects). That's why sometimes the object remains in the weakHash untill the end of this thread execution.
ObjectInputStream istream = null;
try {
istream = new ObjectInputStream(socket.getInputStream());
Fluctuation fluctuation = (Fluctuation) istream.readObject();
//istream.close();
//istream = null;
currentlyProcessedFluctuations.put(fluctuation, "Asset " + fluctuation.getAsset() + " is currently being processed.");

Thread frontOfficeSubcriber = new Thread(new FrontOfficeSubscriber(fluctuation, "FrontOfficeSubscriber-" + fluctuation.getDescription()));
frontOfficeSubcriber.setPriority(Thread.MAX_PRIORITY);
frontOfficeSubcriber.start();


Thread middleOfficeSubcriber = new Thread(new MiddleOfficeSubscriber(fluctuation, "MiddleOfficeSubscriber-" + fluctuation.getDescription()));
middleOfficeSubcriber.start();

String fluctuationName = fluctuation.getDescription();
fluctuation = null;
//realease strong reference on object; now reference is only in the threads. When all Subscriber threads.
//When subscribers release strong reference than object should be collected in the weakHashMap
System.out.println("ServerBroker realeased strong reference on " + fluctuationName + " and calls for gc.");
System.gc();
System.out.println("ServerBroker-"+fluctuationName+": Hoping gc executed. Checking the WeakHashMap size : " + ServerBroker.currentlyProcessedFluctuations.size());

} catch (ClassNotFoundException ex) {
Logger.getLogger(ServerBroker.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(ServerBroker.class.getName()).log(Level.SEVERE, null, ex);
} finally{
try {
istream.close();
} catch (IOException ex) {
Logger.getLogger(ServerBroker.class.getName()).log(Level.SEVERE, null, ex);
}
}

}
}





package fr.bytl.test.reference;
import java.util.*;
import java.lang.ref.*;


/**
*
* @author bdegerat
*/
// this is based on Heinz Kabutz implementation/ I just remooved the most recently used cashed part
public class SoftHashMap extends AbstractMap {

private final Map hash = new HashMap();
private final ReferenceQueue queue = new ReferenceQueue();

public SoftHashMap() {
}

@Override
public Object get(Object key) {
Object result = null;
SoftReference soft_ref = (SoftReference) hash.get(key);
if (soft_ref != null) {
result = soft_ref.get();
if (result == null) {
hash.remove(key);
} else {
}
}
return result;
}

private static class SoftValue extends SoftReference {

private final Object key; // always make data member final

private SoftValue(Object k, Object key, ReferenceQueue q) {
super(k, q);
this.key = key;
}
}

private void processQueue() {
SoftValue sv;
while ((sv = (SoftValue) queue.poll()) != null) {
hash.remove(sv.key);
}
}

@Override
public Object put(Object key, Object value) {
processQueue();
return hash.put(key, new SoftValue(value, key, queue));
}

@Override
public Object remove(Object key) {
processQueue();
return hash.remove(key);
}

@Override
public void clear() {
processQueue();
hash.clear();
}

@Override
public int size() {
processQueue();
return hash.size();
}

@Override
public Set entrySet() {
throw new UnsupportedOperationException();
}
}






/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

package fr.bytl.test.reference;

/**
*
* @author bdegerat
*/
public abstract class Subscriber implements Runnable {

protected String subscriberName;
protected volatile Fluctuation fluctuation;

public Subscriber(String threadName) {
this.subscriberName = threadName;
}

public Subscriber() {
}

public Subscriber(Fluctuation fluctuation, String subscriberName) {
this.subscriberName = subscriberName;
this.fluctuation = fluctuation;
}


public void run() {
doYourThing();
String objectName = fluctuation.getDescription();
this.fluctuation = null;
System.out.println(subscriberName + " realeased strong reference on " + objectName + " and calls for gc.");
System.gc();
System.out.println(subscriberName + ": Hoping gc executed. Checking the WeakHashMap size : " + ServerBroker.currentlyProcessedFluctuations.size());
}
abstract void doYourThing();
}












Andrei Matyas
Greenhorn

Joined: Apr 15, 2007
Posts: 25
Hello )

But what about the GC activity when using Weak and Soft Refs ?

WeakReferences

A few time ago I used WeakRefs to speed up gc collection on big old objects.
Basically when minor collection occurs (quite often) and when WeakReference is the only reference to the underlying object, referents are reclaimed immediately even if the object is present on oldGen space. That means that we can free OldGen objects with a quick minor GC.

It will be nice to test it with different GC strategies on different JDK versions and platforms.

SoftReferences

When major collection occurs, and when SoftReference is the only reference to the underlying object, and when memory is low (what the hell does this mean - before an OutOfMemoryException) their referents are reclaimed. This is a dummy form of lazy loading. The question is how the GC decides which SoftReference to collect (based on size, heap time, random ???).

I used SoftReference for caching (in my case the jdbc calls results). Used with a syncro timer (scheduled refresh) it could be a quite simple and efficient caching method.

In this case you are not very portable (behavior may change) between platforms and java versions.
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: a simple WeakReference and SoftReference exemple
 
Similar Threads
Collection Interface
JVM Garbage Collection
java.lang.ref ?
Garbage Collection
plugging in Weak ,Soft and Phantom references