In my previous post, i had brought up the observation that Reference objects cannot be used as reliable keys in Maps/Sets. Intutively I had expected the Reference objects to delegate the equals/hashCode methods to its referrant object.But it was not so. It left me thinking why was it designed not to delegate.
The top reason that comes to my mind is that if the referent object that the Reference points to is GC'ed, then if we are delegating, then equals/hashCode would have to be handled by Reference.Typically it would have do something like
public int hashCode(){
if(get() == null) return 0 or -1;
return get().hashCode();
}
This means that in a map, where keys are references, all the keys(references) whose referents
have been GC'ed, would end up having the same hashCode. This would cause weird runtime issues when the map is being resized and we would have no clue why ! Same hold true when we try to add ReferenceObjects to a Set.
We might also wonder why not then store the hashCode of the referent in a separate variable when creating the reference. Well hashCode can change over the lifetime of an object, meaning we will never know if the stored value is still valid. If it does not change, it will work.
Geez, i've been breaking my head trying to create something like a ConcurrentWeakHashMap and nothing seems to work. So I just made a call and decided my keys have to be objects whose hashCode will not change with data. Makes life more simpler. If at all I need a true ConcurrentWeakHashMap i've decided to use the less performant method of creating a WeakHashmap and doing a Collections.synchronizedMap() !
Thursday, December 29, 2005
Reference Objects and hashcode
Posted by Kaushik at 9:05 AM 1 comments
Labels: Java
Wednesday, December 28, 2005
Reference Objects as Keys in Maps
WeakReference nor its parent Reference implement the hashCode or Equals method. The default implementation of the Object.equals is to check reference equality. So theoretically if i have an object objInstance and i create two WeakReferences out of it and add it to a hashmap it should not overwrite but add it. And it did.
String str1 = "oioioi";
HashMap map = new HashMap();
map.put(new WeakReference(str1),"one");
map.put(new WeakReference(str1),"two");
Map size at end of the above is 2.
Now comes the million $ question...how the heck does a WeakHashMap work properly then if all keys are indeed WeakReferences as the doc specifies?
To unravel the mystery, lets dig a bit into the src of WeakHashMap.
All entries in a Map are made up of Entry objects.
class Entry<k, v> extends WeakReference<k> implements Map.Entry<k, v>
whereas the same in a HashMap looks like
class Entry<k, v> implements Map.Entry<k, v>
The Entry class in both maps implements equals and hashCode methods.
The keys in WeakHashMap (which are strong references) are objects that implement hashcode methods. Internally when storing the keys, they are stored as weak references since Entry extends WeakReference. And when we call equals/hashcode on these WeakReference Entry objects, the subclass Entry has overidden these methods to delegate the call to the actual key object.
Thus the mystery is solved. So essentially it means subclass WeakReference if you want to use it in a Map.
Posted by Kaushik at 4:38 PM 0 comments
Labels: Java
Classloading in Appservers
Anyone creating a web-application in an application server, might have wondered, how the servlet engine did not muddle up classes from different web-apps. Take the case of a jsp index.jsp or com.xxx.Controller which might be present in nearly all web-apps. When we deploy 2 webapps having these two classes, how is the server using the correct index.jsp/Controller, when a request comes in.
The point to note is that since a class is loaded only once by a classloader, Controller if loaded once will stay on in the memory and same copy will be reused. If the application server used only one class loader to load all classes then we would show only one version of index.jsp for all webapps.
To solve this, it becomes clear each web-app needs its own classloader. Good. Now knowing this, I thot if i compile Controller & put it some where on the class path, then each web-app class loader will still refuse to pick up the Controller in its deployed location. Why ? Because the class loader documentation says all classloaders typically delegate the request to the parent before attempting to resolve it itself. The classloader hierarchy looks like this ...Bootstrap classloader
|
Extension classloader (loads extension classes...new version of jaxp.jar..)
|
System classloader (loads classes from classpath)
|
EAR Classloader
|
WAR Classloade
So if the system class loader loads Controller from classpath it prevents all the WAR classloaders from loading Controller ever. The only way out is to prevent system classloader from loading Controller from classpath. How? By using our own implementation of the classloader. This lead me to my next question..how do i swap the default system class loader with my own implementation ?
The answer is quite simple. All we need to do is set the propery java.system.class.loader to the desired system classloader. Say we do java -Djava.system.class.loader=my.test.SimpleClassLoader then the class loader hierarchy now becomes ..
Bootstrap classloader
|
Extension classloader (loads extension classes...new version of jaxp.jar..)
|
Default System classloader (can load classes from classpath)
|
System classloader (my.test.SimpleClassLoader)
|
EAR Classloader
|
WAR Classloader
Then i found out that the system classloaders in most app servers is designed to load only those package names that fit some predefine rules like javax.ejb.* etc and load classes only from those selected packages. They also delegate loading core java classes to the boostrap loader. These classloaders in addition do not delegate to parent for other requests. This solves most of the questions raised !
Quite a long post, makes me remember the stories we wrote in our history exams ! Ok, more details on how default system class loaders work and how class sharing between select web-apps can be done in the future.
For knowing more on classloaders read this article
Posted by Kaushik at 8:53 AM 0 comments
Labels: Java
Wednesday, December 14, 2005
Type Checking Verifier in Java6
In my previous post I talked about different steps involved in loading a class.
The verification process that happens during linking is a very important part of the java sandbox security model.
The verification process does the following
* Checks that every instruction has a valid operation code;
* Checks every branch instruction branches to the start of some other instruction,rather than into the middle of an instruction;
* Checks every method is provided with a structurally correct signature;
* Checks every instruction obeys the type discipline of the Java virtual machine language.
* Checks accesses to objects are always as what they are (for example, InputStream objects are always used as InputStreams and never as anything else).
* Access restrictions are not violated
* Plus a lot of other things as specified in the JVM specs.
As you can guess this process would be very time intensive. Java 6 has reportedly come with a new 'Type Checking Verifier' that promises to dramatically reduce the run-time computational requirements of the bytecode verifier on Java SE.
How does it manage to do it?
It turns out that much of the program analysis that the verifier performs at run-time may not be necessary, because the type information can also be obtained at compile-time when the class file is generated. The compiler includes type information needed by the verifier in the class file, the job of the verifier at run-time is made much simpler and less memory intensive. The verifier is handed a table of expected program states encoded in the class file, and can validate that those expected states actually occur, and result in safe execution.
More details on it, once i get a chance to read the updated JVM specs for this feature.
Posted by Kaushik at 8:06 AM 0 comments
Labels: Java
Java Program Execution
The favourite interview question one of my friends used to ask was what happens when a java program is run. The answer to this question would set our expectations from the guy in front of us immediately. Most did not even get close.
So what is the answer?
Three important steps happen before main is invoked - loading, linking and initialization.
Loading
- Loads the class byte codes and create a Class object.
- verification - binary representation of a class or interface is structurally correct
- preparation - creating the static fields for a class or interface and initializing such fields to the default values
- resolution -symbolic references to other classes and interfaces and their fields, methods, and constructors is checked to be correct and, typically, replaced with a direct reference that can be more efficiently processed if the reference is used repeatedly
- Initialization of a class consists of executing its static initializers and the initializers for static fields
- Initialization of an interface consists of executing the initializers for fields (constants) declared
- Synchronize on the Class object that represents the class or interface to be initialized since other threads may be attempting to init the same class.
Posted by Kaushik at 7:45 AM 0 comments
Labels: Java
Tuesday, December 13, 2005
JMM
Whats the new JMM (JSR-133) all about?
Apparently the rules that govern the java memory these days have been changed. A brief list of the new rules:
- Volatile read/write instructions cannot be reordered with other instructions
- Synchronized blocks cannot be executed out of sequence with each other
- Writes that initialize final fields will not be reordered with operations following the freeze
- Every entry into a synchronised blocks trigger a flush of working memory so that all subsequent calls goto main memory
- Every exit out of synchronised blocks trigger a write of working memory to main memory
- All actions in a thread t1 should happen before any other thread (t2 say), successfully returns from a
Thread.join()
on thread t1.
Posted by Kaushik at 9:52 AM 0 comments
Labels: Java