Monday, March 15, 2010

Invisible object and memory leak

It's been quite long time I am involved in java development but I have never heard about invisible objects. We mainly know about unreachable objects while going through garbage collection mechanism in java.

Invisible objects are quite different than unreachable objects in following respects:

1. unreachable objects are those objects which doesn't has any references to them but in case of invisible object they have strong references assoicated with them but thses refrences are not available to any thread stack, making object invisible to garbage collector.
2. unreachable objects are eligible for GC and normally don't cause memory leak, while invisible objects are not eligible for gorbage collection causing memory leak
3. invisible objects are result of efficient implemenation of JVM which don't nullify references if they are out of scope unless the method is thrown from thread stack upon execution.

Lets take one exampe to explain it more clearly

public void run(){

try{

MyOject o = new MyObect();//1
o.callMyMethod();

} catch (Exception e){}
//do lot of other time taking things
}

at line 1 we have created an object, ideally when thread will complete try block, reference "o" will go out of scope and should be removed from stack making MyObject unreachable so eligible for garbage collection. But an efficient jvm implementation will not do that, it will retain the reference "o" referring to MyObject unless run method is executed and thrown out of thread stack. Since reference "o" is not available to thread's stack so it's invisible to garbage collector.

The best way to avoid this situation is set the reference "o" explicitly null in try block once we are done with it.

Wednesday, March 10, 2010

Use of noLocal parameter while creating subscriber in JMS

Lets examine jms api for creating subscriber.

public TopicSubscriber createSubscriber(Topic topic, String messageSelector, boolean noLocal) throws JMSException

java doc says that third parameter is used to make a decision if subscriber want to consume message published by itself (if same connection is used to create publisher which publishes the message) or not.

Not very much clear! Lets design an experiment to see its behavior.

I will create one chat application with following details

topic: HowToEstablishPeaceInWorld

subscriber 1: Osama Bin Laden

subscriber 2: George Bush

This chat application will be based on JMS. Both these subscribers are publishers as well. One will type one's GREAT idea on one's console and it will be visible to other.

The pseudo code below shows how subscribers have subscribed the topic.

Topic chatTopic = (Topic)jndi.lookup("HowToEstablishPeaceInWorld");

TopicSubscriber OsamaBinLaden = subSession.createSubscriber(chatTopic);

TopicSubscriber GeorgeBush = subSession.createSubscriber(chatTopic);

This is Bush console
Bush: hi
Bush says: hi
osama says: hello

This is osama console

Bush says: hi
osama: hello
osama says: hello

The problem here is, the subscribers are consuming the message posted by themselves. Now lets modify the code and see the change in output

TopicSubscriber subscriber = subSession.createSubscriber(chatTopic,null,true);

This is Bush console
Bush: hi
osama says: hello

This is osama console
Bush says: hi
osama: hello

Hope you noticed the difference subscribers are no more consuming message posted by themselves(no more Bush says to Bush console).

Lets see how chat on world peace goes further.

Bush console

Bush: U still alive?

osama console

Bush says: U still alive?

Now osama thinks doesn't jms offers a way so that I don't see what Bush is typing but bush will see everything I type. He contacted software wing of al-Qaeda and they didn't let osama down.

The second argument in create Subscriber can be used to filter message.

Find below the source code for chat program. The code is in respect to jboss MQ server.


import javax.jms.*;
import javax.naming.*;
import java.io.*;
import java.io.InputStreamReader;
import java.util.Properties;

public class Chat implements javax.jms.MessageListener{
private TopicSession pubSession;
private TopicSession subSession;
private TopicPublisher publisher;
private TopicConnection connection;
private static String username;

/* Constructor. Establish JMS publisher and subscriber */
public Chat(String username)
throws Exception {
// Obtain a JNDI connection
Properties env = new Properties( );
// ... specify the JNDI properties specific to the vendor
env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
env.put(Context.PROVIDER_URL, "jnp://127.0.0.1:1099");
env.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");



InitialContext jndi = new InitialContext(env);

// Look up a JMS connection factory
TopicConnectionFactory conFactory =
(TopicConnectionFactory)jndi.lookup("TopicConnectionFactory");

// Create a JMS connection
TopicConnection connection =
conFactory.createTopicConnection();

// Create two JMS session objects
TopicSession pubSession =
connection.createTopicSession(false,
Session.AUTO_ACKNOWLEDGE);
TopicSession subSession =
connection.createTopicSession(false,
Session.AUTO_ACKNOWLEDGE);

// Look up a JMS topic
Topic chatTopic = (Topic)jndi.lookup("topic/HowToEstablishPeaceInWorld");

// Create a JMS publisher and subscriber
TopicPublisher publisher =
pubSession.createPublisher(chatTopic);
TopicSubscriber subscriber =
subSession.createSubscriber(chatTopic,null,true);

// Register the jms message listener
subscriber.setMessageListener(this);

// Intialize the Chat application
set(connection, pubSession, subSession, publisher, username);

// Start the JMS connection; allows messages to be delivered
connection.start( );

}
/* Initialize the instance variables */
public void set(TopicConnection con, TopicSession pubSess,
TopicSession subSess, TopicPublisher pub,
String username) {
this.connection = con;
this.pubSession = pubSess;
this.subSession = subSess;
this.publisher = pub;
this.username = username;
}
/* Receive message from topic subscriber */
public void onMessage(Message message) {
try {
TextMessage textMessage = (TextMessage) message;
String text = textMessage.getText( );
System.out.println(text);
} catch (JMSException jmse){ jmse.printStackTrace( ); }
}
/* Create and send message using topic publisher */
protected void writeMessage(String text) throws JMSException {
TextMessage message = pubSession.createTextMessage( );
message.setText(username+" says: " +text);
publisher.publish(message);
}
/* Close the JMS connection */
public void close( ) throws JMSException {
connection.close( );
}
/* Run the Chat client */
public static void main(String [] args){
try{

Chat chat = new Chat(args[0]);

// Read from command line
BufferedReader commandLine = new
java.io.BufferedReader(new InputStreamReader(System.in));

// Loop until the word "exit" is typed
while(true){
String s = commandLine.readLine( );
if (s.equalsIgnoreCase("exit")){
chat.close( ); // close down connection
System.exit(0);// exit program
} else
chat.writeMessage(s);
}
} catch (Exception e){ e.printStackTrace( ); }
}
}