Monday, January 24, 2005

Java Tip #7 - Use .hotspot_compiler file to stop compilation

The hotspot compiler has known bugs that can cause the compilation thread to go cpu bound. We had seen this behavior occasionally in production. A customer would call & report that no one was logged into the system, but it was at 100% on one cpu. The instance would normally be restarted & things would be fine. We couldn't figure out why & of course the customer was frustrated at having to restart the system. I can't blame 'em. For a time we had no clue what was causing the problem.

Recently we did some load testing at Sun's Market Development Engineering lab (very cool, btw) and really stressed our application. We kept seeing this "java/9" thread go to 100% cpu during the warmup period. It didn't happen every time, but it happened often enough to slow us down. The busy process was visible in prstat using the -L flag to list lightweight processes (lwp). We used pstack to look at the offending lwp & saw it was the hotspot compiler. We applied the +XX:PrintCompilation vm flag and found that the compilation was stopping at various times & it always seemed to happen on the same methods. We used the .hotspot_compiler file to exclude various methods and the stuck thread problem was solved.

The .hotspot_compiler file goes in working directory & has a line for each method to be excluded.
For example:
exclude com.whatever.TheClassName theMethodName

This would prevent the com.whatever.TheClassName.theMethodName() from being compiled.

Here's a "more" listing of a file with a single entry.

$ more .hotspot_compiler
#http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5043395
exclude oracle/jdbc/driver/OraclePreparedStatement executeBatch
And the console output from the compiler.
### Excluding compile:  oracle.jdbc.driver.OraclePreparedStatement::executeBatch

This bug report is similar to the problem we had.
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5043395

According to the bug report, this is resolved in tiger aka 1.5. When we rerun our load tests in June 2005, I will know for sure.


Thursday, January 13, 2005

Java Tip #6 - Don't capitalize first two letters of a bean property name

This is in our java standards. You should not create a java bean property name that begins with a capital letter in the 1st two places. It can lead to confusing results. We had this happen a few times & finally added it to our standards & enforce it in code reviews. One place we saw problems was in struts. The form bean properties are used in the JSP page, but the Struts framework has to use the getter() & setter() to interact with the bean. This mapping happens based on the java bean spec & in certain cases can cause a method not found error if the developer doesn't name the method just right. The java bean spec provides guidelines on how to map between property and the associated getter() & setter().

The following is from the javabean spec at http://java.sun.com/products/javabeans/docs/beans.101.pdf .

8.8 Capitalization of inferred names.

Thus when we extract a property or event name from the middle of an existing Java name, we normally convert the first character to lower case. However to support the occasional use of all upper-case names, we check if the first two characters of the name are both upper case and if so leave it alone. So for example,

“FooBah” becomes “fooBah”
“Z” becomes “z”
“URL” becomes “URL”

We provide a method Introspector.decapitalize which implements this conversion rule.

Here’s the source code for Introspector.decapitalize().


public static String decapitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
Character.isUpperCase(name.charAt(0))){
return name;
}
char chars[] = name.toCharArray();
chars[0] = Character.toLowerCase(chars[0]);
return new String(chars);
}
The following table shows how the 1st two characters map. If this was a mathematical function, I’d say it wasn’t one-to-one. (‘aa’ and ‘Aa’ imply the same getter)

property
-----------
getter
---------
aa getaa()
aA getaA()
Aa getaa()
AA getAA()


Tuesday, January 11, 2005

Java Tip #5 - Avoid 64KB method limit on JSP

Ok, this is hard to do on purpose with plain old java, but I've seen plenty of JSPs that when compiled had a method that exceeded the 64KB limit. Depending on the VM it may just barf and end with a hotspot error or it might give you some kind of useful message. I find it's just a hotspot error. Tomcat describes this problem in their release notes.

The simplest way to fix this is to break the page into parts so that each compiles separately using a jsp include. Avoiding big, monolithic JSPs is easy if you use Struts with Tiles. Of course, this shows how behind I am now since I'm not using one of the newer frameworks.

This seems to be less of a problem as container vendors have improved. They all generate different code, so it varied in our environment from Tomcat, SilverStream, and BEA. At one time or another, we had JSPs that exhibited the problem on one of the three platforms, but not the other two. I am happy to say we now target only one vendor, BEA.

Here is the blurb from section 4.10 of the Java Language Specification (JLS).
The amount of code per non-native, non-abstract method is limited to 65536 bytes by the sizes of the indices in the exception_table of the Code attribute (§4.7.3), in the LineNumberTable attribute (§4.7.8), and in the LocalVariableTable attribute (§4.7.9).
Note that using the @include file directive does not solve the problem, since it just inlines the JSP. You need to use a jsp include or tiles or similar.

Java Tip #4 - Endorsed Standards Override Mechanism

An endorsed standard is a Java(TM) API defined through a standards process other than the Java Community Process(SM). These standards might be revised between releases of the Java 2 platform. Sun defined the Endorsed Standards Override Mechanism (ESOM) to allow a developer to provide a newer version of an endorsed standard than those include in the Java 2 platform.

The classes that need to be overridden should be jarred up & placed in one or more directories specified by the java.endorsed.dirs System property. If unspecified, then the default location is

[java-home]\lib\endorsed - Microsoft Windows
[java-home]/lib/endorsed - Solaris or Linux

If more than one directory is specified, they must be separated by File.pathSeparatorChar.

Below is the list of packages as of 1.4 & 1.5 that can use the ESOM to provide newer implementations.
javax.rmi.CORBA

org.omg.CORBA
org.omg.CORBA.DynAnyPackage
org.omg.CORBA.ORBPackage
org.omg.CORBA.portable
org.omg.CORBA.TypeCodePackage
org.omg.CORBA_2_3
org.omg.CORBA_2_3.portable
org.omg.CosNaming
org.omg.CosNaming.NamingContextExtPackage
org.omg.CosNaming.NamingContextPackage
org.omg.Dynamic
org.omg.DynamicAny
org.omg.DynamicAny.DynAnyFactoryPackage
org.omg.DynamicAny.DynAnyPackage
org.omg.IOP
org.omg.IOP.CodecFactoryPackage
org.omg.IOP.CodecPackage
org.omg.Messaging
org.omg.PortableInterceptor
org.omg.PortableInterceptor.ORBInitInfoPackage
org.omg.PortableServer
org.omg.PortableServer.CurrentPackage
org.omg.PortableServer.POAManagerPackage
org.omg.PortableServer.POAPackage
org.omg.PortableServer.portable
org.omg.PortableServer.ServantLocatorPackage
org.omg.SendingContext
org.omg.stub.java.rmi
org.w3c.dom
org.xml.sax
org.xml.sax.ext
org.xml.sax.helpers

Java Tip #3 - Don't use fields on Struts action classes

This is very specific to the Struts web application framework. Don't use a field on a Struts action class. Struts caches action classes & reuses them to call the execute() method. (perform() on 1.0) This can cause unexpected behavior & performance issues under a load. Add a check for this to your code review process.

Thanks to the anonymous person who pointed out even without the 'static' modifier, this is a bad idea. Unless the field is marked as final, just don't do it.




Monday, January 10, 2005

Java Tip #2 - Contiguous Switches

When coding a switch statement, use contiguous ranges even if they are dummies. The compiler can optimize switches that use contiguous ranges. If you decompile to bytecode using DeCafe or similar, you SHOULD see the "tableswitch" bytecode where this switch begins. If you see "lookupswitch", it will use a O(n) lookup instead of directly jumping to the statement. I've seen nice performance increases using this technique for large switch statements.

Here is an example class with three methods. The method switchTest1 has contiguous values for 1-5. The method switchTest2 has values for 1 and 3-5 and the final switchTest3 has values for 1,3,4, and 128. This is a nonsensical class, but does show the behavior.

The results are that:
1) switchTest1 uses the desired "tablelookup".
2) switchTest2 is modified by the compiler to add the missing "2" to the default case and thus uses the desired "tablelookup" bytecode.
3) switchTest2 is too complicated for the compiler to resolve, so it falls back to the "lookupswitch" bytecode.

Below is a decompile with bytecode annotations from DeCafe of a 1.4.2 built class. Need to test this in 1.5.


public class SwitchTest
{

public SwitchTest()
{
// 0 0:aload_0
// 1 1:invokespecial #9
// 2 4:return
}

public long switchTest1(int n)
{
long result = 0L;
// 0 0:lconst_0
// 1 1:lstore_2
switch(n)
//* 2 2:iload_1
{
//* 3 3:tableswitch 1 5: default 69
// 1 36
// 2 41
// 3 48
// 4 55
// 5 62
case 1: // '\001'
result = 1L;
// 4 36:lconst_1
// 5 37:lstore_2
break;

//* 6 38:goto 74
case 2: // '\002'
result = 4L;
// 7 41:ldc2w #16
// 8 44:lstore_2
break;

//* 9 45:goto 74
case 3: // '\003'
result = 9L;
// 10 48:ldc2w #18
// 11 51:lstore_2
break;

//* 12 52:goto 74
case 4: // '\004'
result = 16L;
// 13 55:ldc2w #20
// 14 58:lstore_2
break;

//* 15 59:goto 74
case 5: // '\005'
result = 25L;
// 16 62:ldc2w #22
// 17 65:lstore_2
break;

//* 18 66:goto 74
default:
result = n * n;
// 19 69:iload_1
// 20 70:iload_1
// 21 71:imul
// 22 72:i2l
// 23 73:lstore_2
break;
}
return result;
// 24 74:lload_2
// 25 75:lreturn
}

public long switchTest2(int n)
{
long result = 0L;
// 0 0:lconst_0
// 1 1:lstore_2
switch(n)
//* 2 2:iload_1
{
//* 3 3:tableswitch 1 5: default 62
// 1 36
// 2 62
// 3 41
// 4 48
// 5 55
case 1: // '\001'
result = 1L;
// 4 36:lconst_1
// 5 37:lstore_2
break;

//* 6 38:goto 67
case 3: // '\003'
result = 9L;
// 7 41:ldc2w #18
// 8 44:lstore_2
break;

//* 9 45:goto 67
case 4: // '\004'
result = 16L;
// 10 48:ldc2w #20
// 11 51:lstore_2
break;

//* 12 52:goto 67
case 5: // '\005'
result = 25L;
// 13 55:ldc2w #22
// 14 58:lstore_2
break;

//* 15 59:goto 67
case 2: // '\002'
default:
result = n * n;
// 16 62:iload_1
// 17 63:iload_1
// 18 64:imul
// 19 65:i2l
// 20 66:lstore_2
break;
}
return result;
// 21 67:lload_2
// 22 68:lreturn
}

public long switchTest3(int n)
{
long result = 0L;
// 0 0:lconst_0
// 1 1:lstore_2
switch(n)
//* 2 2:iload_1
{
//* 3 3:lookupswitch 4: default 70
// 1: 44
// 3: 49
// 4: 56
// 128: 63
case 1: // '\001'
result = 1L;
// 4 44:lconst_1
// 5 45:lstore_2
break;

//* 6 46:goto 75
case 3: // '\003'
result = 9L;
// 7 49:ldc2w #18
// 8 52:lstore_2
break;

//* 9 53:goto 75
case 4: // '\004'
result = 16L;
// 10 56:ldc2w #20
// 11 59:lstore_2
break;

//* 12 60:goto 75
case 128:
result = 16384L;
// 13 63:ldc2w #30
// 14 66:lstore_2
break;

//* 15 67:goto 75
default:
result = n * n;
// 16 70:iload_1
// 17 71:iload_1
// 18 72:imul
// 19 73:i2l
// 20 74:lstore_2
break;
}
return result;
// 21 75:lload_2
// 22 76:lreturn
}
}

Java Tip #1 - Constant Comparison

I'm going to start writing down some of the java tidbits I've picked up over the years. I find I'm starting to forget some of them... so this serves as a permanent reminder for myself and possibly education for some. Many of these will seem trivial and I doubt any are original ideas.

When comparing a constant to a variable, always put the constant on the left side of the expression. This will prevent NullPointerExceptions and avoid extra comparisons to null.


This has the potential to generate an NPE.
public function void greeting1() {
if (test.equals("hello"))) {
System.out.println("hello")
} else {
System.out.println("goodbye");
}
}

This prevents the NPE with a comparison to null.
public function void greeting2() {
if (test != null && test.equals("hello"))) {
System.out.println("hello")
} else {
System.out.println("goodbye");
}
}

This is safe from an NPE & avoids the null barrier.
public function void greeting3() {
if ("hello".equals(test)) {
System.out.println("hello")
} else {
System.out.println("goodbye");
}
}

In a code review, I typically will make a developer switch to the style in greeting3 if there is a chance of an NPE. If it's already guarded by a comparison to null, I'll point it out but let the code pass review. Some people might actually prefer the explicit comparison to null for readability.