Wednesday, January 09, 2008

Experience using apache commons EqualsBuilder class

Following are my learnings on how to use the EqualsBuilder class in apache commons lang library.

C:\Work\EqualsProto\src\equalsproto\Main.java


package equalsproto;

//~--- non-JDK imports --------------------------------------------------------

import java.util.Arrays;
import org.apache.commons.lang.builder.EqualsBuilder;


class A
{
private String s = "Watsh";
private int x = 10;
private float y = 20.2f;

//~--- constructors --------------------------------------------------------

A() {}

A(int i, int i0, String string)
{
this.x = i;
this.y = i0;
this.s = string;
}

//~--- methods -------------------------------------------------------------

/**
* Method description
*
*
* @param obj

*
* @return
*/
@Override
public boolean equals(Object obj)
{
if (obj instanceof A == false) {
return false;
}

if (this == obj) {
return true;
}

A rhs = (A) obj;

/** Note:

* Do not use appendSuper when the super class is java.lang.Object as

* default implementation of equals in Object class will return true only

* when two references are pointing to the same object instance and hence

* the effect is not desirable.
*/
return new EqualsBuilder().append(s, rhs.s).append(x,
rhs.x).append(y, rhs.y).isEquals();
}
}



class B
{
private String z = "Rajneesh";
private A a;
private A[] array;

//~--- constructors --------------------------------------------------------

B(String z, A a, A[] array)
{
this.z = z;
this.a = a;
this.array = array;
}

@Override
/**
* Learning:
* 1. appendSuper() should not be used as it then calls super.equals()

* for java.lang.Object class which will return true only when both lhs and

* rhs references point to the same object instance and hence will return false

* when the 2 object instances are different but meaningfully equivalent.

*
* 2. To compare arrays, you will either need to use the

* EqualsBuilder.reflectionEquals() approach or if you are using the

* EqualsBuilder.append() approach then append(array1, array2) calls

* array1.equals(array2) which will only do a shallow comparison for the

* 2 arrays involved. So in such a case, you must use Arrays.deepEquals() for

* all array members of your class and once that equality is met you can use

* EqualsBuilder.append() for rest of the non-array instances.

*
* NOTE: I have not tested for how this approach works for Collection classes.

*/
/*public boolean equals(Object obj)
{
if (obj instanceof A == false) {
return false;
}

if (this == obj) {
return true;
}

B rhs = (B) obj;

return new EqualsBuilder().append(z, rhs.z).append(a,

rhs.a).append(array, rhs.array).isEquals();
}*/
public boolean equals(Object obj) {
return EqualsBuilder.reflectionEquals(this, obj);
}
}



/** Testing the use of EqualsBuilder.
*
* @author wrajnees

*/
public class Main
{

/**
* @param args the command line arguments

*/
public static void main(String[] args)
{
A a1 = new A(2, 43, "xxx");
A a2 = new A(2, 43, "xxx");
if (a1.equals(a2)) {
p("a1 equals a2");
} else {
p("a1 not equals a2");
}

A[] array1 = new A[2];
A[] array2 = new A[2];

array1[0] = a1;
array1[1] = new A();

array2[0] = a2;
array2[1] = new A();

// comparing arrays

if (Arrays.deepEquals(array1, array2)) {
//if (array1.equals(a2)) { // -- does not work
p("arrays are equal");
} else {
p("arrays arent equal");
}

// comparing more complex object with containment and array

B b1 = new B("Test", a1, array1);
B b2 = new B("Test", a2, array2);
if (b1.equals(b2)) {
p("b1 equals b2");
} else {
p("b1 not equals b2");
}
}

private static void p(String s) {
System.out.println(s);
}
}



Tuesday, January 08, 2008

Detecting memory leaks in Java SE

Today i have learned about a nice approach to detecting memory leaks in the Java SE applications using the jmap and jhat (Java Heap Analysis Tool). The process to be followed is described below:

1. Run your application.

2. Run the command jps to know the process id of the J2SE application you ran.
% jps
1234 MyApp
...

3. Perform those actions in your application which you feel will cause the memory leak. You can observe the real time heap usage plot in jconsole. Launch jconsole and select your application in it to connect to.
% jconsole

4.Then run the command jmap to dump the heap.
% jmap -dump:file=myapp.bin 1234

This will produce a heap dump in myapp.bin file with the heap profile.

4. Run the JHAT (Java Heap Analysis Tool) as follows:
% jhat -J-mx512m heap.bin

The above command starts a small Http server at port 7000 by default.

5. Browse to http://localhost:7000 and you will have you the heap browser - a hyperlinked set of pages from where you can trace every object allocated and who all reference the object at the point at which the heap dump was created.

6. The important pages to browse to are:
http://localhost:7000/histo/ - to see the histogram of heap usage.
http://localhost:7000/showInstanceCounts or http://localhost:7000/showInstanceCounts/includePlatform/ to see biggest types with most object allocations (ie instances).

and some advanced features of using SQL to query values of instance members:
http://localhost:7000/oql/

7. So browse to http://localhost:7000/showInstanceCounts/. Investigate "Instances" and not "Classes". Use “Reference Chains from Rootset” (Exclude weak refs!!!) to see who’s holding the instance. This tip i found in one of the links below in the reference section and it really was what was required to find the memory leaking code.

Some good references are:
  1. Memory leaks in Java program
  2. Using Mustang's jmap/jhat to profile Glassfish
  3. Finding Memory leaks in Java Program

Book notes: Designing Data-Intensive Applications: The Big Ideas Behind Reliable, Scalable, and Maintainable Systems, by Martin Kleppmann

My notes from the excellent book on how software has evolved to handle data from hierarchical databases to the NoSQL -  https://www.goodrea...