Saturday, March 15, 2008

How to file a good bug report

How to file a good bug report

One of the first lessons, hard lessons, you learn coming into the world of software development out of college is how hard it can be to understand bug reports.

Variously they can be vague, misleading, inaccurate, confusing or best of all - misunderstanding a feature for a bug (the latter often points to usability issues).

Having endured many of these and now doing a fair bit of testing of my current application, I try to "be the change I wish to see in the world". So I've come up with the following scheme for bug reports.

#1 Start with a good short bug summary
For example:
"Data Entry screen disappears after entering certain unusual keystrokes"

The good part of this is a manager / team lead gets a quick idea of how serious this issue is (screen disappearing - bad!) where it occurs (data entry screen) and what caused it (unusual keystrokes). Useful in understanding just how serious it is and how soon it should be reviewed and/or fixed.

Next up you need to focus on the needs of the developer - basically instructions so the developer can reproduce the issue

#2 The *exact* build you are using
e.g.
March 27th 18:05 PM

Sometimes developers get dupe issues with related if not similar symptoms and having the exact build they can often realize "Hey that's that same issue I fixed yesterday" and they can then tell the tester - try the 28th build because it's related to issue X we resolved.

#3 Environmental Aspects
Including
- What machine the different parts of the system were running on e.g. web server, GUI etc.
- What database you were pointing at and DB login if appropriate
- How you installed the build e.g. options
- Your GUI login if appropriate
- If you can, it's really awesome to leave that build around and up-and-running so a developer can come over to your area and check it out for themselves. Not always possible but for those big critical issues it's a real time saver.

#4 Instructions to see the bug
The steps need to be detailed, step-by-step.

Often when I report a bug I spend a few minutes trying to get the least number of clear steps to reproduce an issue. This is important not only for the developer but for the person who subsequently tests the fix perhaps minutes, day or even months later.

You don't want to be too detailed "point your mouse to the 'Send' button and left-click" or too vague "enter some data and hit 'Send'"

1) Login to the GUI
2) Go to 'Tools > Data Entry > Advanced'
3) Select the 'Schedule' tab
4) Enter '03/04/08' and then without saving try to tab off
5) You get a System error (see attached)

5 Supporting Documentation
Screenshots are great 'proof' - especially for those developers you may know, who try to reproduce the issue for 5 seconds and then say "Unable to reproduce" and cancel your bug which you spent quite a bit of time entering. Very annoying!

Some folks who have long complicated steps often choose to add video capture too which is great (but bulky).

Logs are great too especially when they contain more useful errors related to things the developer can grasp e.g. Malformed SQL on line 223 of DataEntry.java.

Often these are attachments and as a technical lead I often trawl through the logs to find the most important lines e.g. often the first stack trace and then copy-and-paste that into the main bug description.

Other things you need in a bug report
- A unique identifier for this bug report
- Priority - Stick to High, Medium and Low - beyond that lies madness! :-)
- Severity is optional - often I find it confused with Priority for various reasons- again three levels should suffice - major, moderate and minor/cosmetic
- Who created the issue and when?
- Who is assigned the item?
- Who is the tester going to be?

Good bug reports take time. But it's worth it - I figure for every minute I put in to the report to make it clear and concise I'm probably saving 5 to 10 minutes of a developer's time and then more for the person who might have to test it (might be me 6 months from now).

As a team lead also I'll reject reports that lack these details. It's funny I've known some experienced QA folks who just can't seem to get this right - it's very frustrating to start a back and forth - which builds is this - please add logs - can you add a screenshot?- and then there are some who write them so well it just becomes a slam-dunk for the developer. Often they're the ones sitting right beside the developer - they've each learned how each other works - how to keep each other productive and keep frustrations to a minimum. That's good all around.

Friday, March 14, 2008

14 Rules for Faster-Loading Web Sites

14 Rules for Faster-Loading Web Sites

These rules are the key to speeding up your web pages. They've been tested on some of the most popular sites on the Internet and have successfully reduced the response times of those pages by 25-50%.

The key insight behind these best practices is the realization that only 10-20% of the total end-user response time is spent getting the HTML document to the browser. You need to focus on the other 80-90% if you want to make your pages noticeably faster. These rules are the best practices for optimizing the way servers and browsers handle that 80-90% of the user experience.

These pages are the companion web site for the book High Performance Web Sites. The examples referenced in the book are hosted here. Navigate through the rules listed below to find the associated examples. Each rule page also contains a link to the Yahoo! Developer Network Performance Blog. There you will find a brief summary of the rule along with comments.

Read more:

Java Interview Questions

Overview

These are some of the trickier questions you probably shouldn't ask in an interview.

Accidentally advanced questions

These questions come from posted interview questions where the question is confused but might have an interesting answer.

How do you free memory in Java?

simple: You can't, the GC cleans up free objects.

intermediate: You can set known references to null and call System.gc() . This later doesn't guarantee a clean up, but Sun's JVM does actual do a GC each time this is called.

advanced: the sun.misc.Unsafe class allows you to allocate, reallocate and free a block of memory. This type of memory has to be explicitly freed with freeMemory()

Can you instantiate the Math class.

simple: No. The constructor for Math is private and all its methods are static.

intermediate: Yes. You can create an instance of a Math class using reflections. However, you cannot instantiate an object which is the Math class using reflections. Note: Class has a private constructor but reflections won't allow you to construct a class this way.

advanced: You can create a class from byte-code by using reflection to call the defineClass method on the ClassLoader. Each ClassLoader can have only one class for a given name, but you can have any number of ClassLoaders . In this way you can instantiate multiple Math class objects.
However you shouldn't want to.

Is there a sizeof operator in Java?

Answers

simple: No, sizeof is not a keyword, nor is there a keyword replacement.

intermediate: You can estimate the size of an object by looking at the amount of memory used before and after an object is created. The GC can make this process a bit random but if you do this enough times you can work it out. (The median is usually right)

advanced: The Instrumentation.getSizeOf() will do this for you. It a bit tricky to setup because you have to have a premain (called before main)

Which class is the superclass of every class?

simple: Object

intermediate: The superclass of Object is null.

advanced: The super class of primitives, interfaces and void is null.

If all methods of a class are synchronized what are the ways you can have more than one thread in methods of a class.

simple: Objects are locked, not methods so different objects can be accessed by different threads. static methods synchronize on the class objects so even if you have one object you can have two threads in different methods.

intermediate: When wait() is called, the lock on an object is released. So while a threads are wait'ing another threads can be holding the lock.

advanced: the sun.misc.Unsafe class can be used to discretely release a lock with exitMonitor() and re-acquire it with enterMonitor(). However using two or more synchronized blocks would be clearer and safer. Another option would be to use a Lock which can be acquired and released without a block.

How can one prove that the array is not null and empty?

simple: array != null && array.length == 0

intermediate: Say you have a method where you want to be able to test int[] or Object[]

public static  boolean isEmpty(ArrayType array) {
return array != null && Array.getLength(array) == 0;
}

Note: the use of the generic ArrayType is just syntactic sugar

Are methods in Java virtual?

simple: By default methods work in a similar way to C++ virtual methods and abstract methods work like C++ pure virtual methods.

intermediate: static methods can be hidden but not overridden. final methods cannot be overridden and only virtual if the override a super classes method or implement an interface.

advanced: By generating byte code, the implementation of a method can be determined/optimised at runtime. In this way you can have an interface with no implementations and still create an instance at runtime.

How do you take a deep copy of an Object.

simple: If it is Cloneable call clone().

intermediate: Cloneable does guarantee a deep copy depending on the implementation in each object, however you can Serialize and then deserialize the object to get a deep copy.

advanced: If the object is neither Cloneable nor Serializable, you can use reflections to extract each field, descending into object to copy the whole structure. Watch out for recursive references! Note: sun.misc.Unsafe.allocateInstance() creates an object without calling a constructor. (used by ObjectInputStream).

Can you change the reference of the final object?

simple: no.

intermediate: With reflections, yes. However, some final constants have optimised such that they are no longer used directly so changing it won't do what you think it might.

Coding questions.

What is a simple thread safe way to lazily construct a singleton without using synchronized or locks.

Use an inner class to create the instance. The class isn't loaded until it is referenced and JVM guarantees the class will be loaded only once.

public class Singleton {
static class SingletonHolder {
static final Singleton SINGLETON = new Singleton();
}

private Singleton() {
System.out.println("new Singleton()");
}

public static Singleton getSingleton() {
return SingletonHolder.SINGLETON;
}

public static void main(String... args) {
System.out.println("main() started.");
getSingleton();
}
}

Edge cases.

For which values of x is this true x == -x && x != 0

Byte.MIN_VALUE, Short.MIN_VALUE, Integer.MIN_VALUE, Long.MIN_VALUE. In these cases, the - operator causes an overflow which results in the same value.

When is this true x + 0 != x

When x is a String, or Float.NaN or Double.NaN.

Why is (Integer) 0 == (Integer) 0 but (Integer) 128 != (Integer) 128.

In this case we are comparing object references, not values. The first case works because autoboxing is performed by the Integer.valueOf() method which caches small values, so the references are the same. However 128 is not cached so the references are different.

What will this do for(int i = Integer.MIN_VALUE; i <= Integer.MAX_VALUE; i++) /* something */ do?

Loop forever. i <= Integer.MAX_VALUE is always true. The alternative is

int i = Integer.MIN_VALUE;
do {
/* something */
} while (i++ < Integer.MAX_VALUE);

Note: this takes less than 6 seconds on a fast PC.

Java Trivia

Are true, false and null keywords.

No, they are reserved words and literals not keywords.
http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.9

Are there any keywords which don't do anything?

const and goto are keywords but cannot be used.
http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.9

Corrected answers

Here are what I believe are the correct answers to the following questions.

How could Java classes direct program messages to the system console, but error messages, say to a file?

http://java.sys-con.com/read/48839.htm

System.setErr(new PrintStream("error.log"));

There is no Stream class and setting System.setOut() will means program messages will go the file not the console.

If a class is located in a package, what do you need to change in the OS environment to be able to use it?

Nothing. The classpath is typically set on the command line with the -cp option.

How many ways can one write an infinite loop ?

http://interviewjava.blogspot.com/2007/04/can-you-change-reference-of-final.html

There is very large, but finite number of infinite loops. A method is limited to 64KB of byte code, so less than 256^65536 possible infinite loops.

The simplest ones are
for(;; );
while(true);
do { } while(true);

How a dead thread can be started?

simple:
There is no dead state for a thread.
For a terminated Thread, isAlive() = false, getState() == State.TERMINATED.

intermediate:
You can start a new thread with the same name using the same Runnable object, or uglier the old Thread as the Runnable for the new Thread.

Thread newThread = new Thread(oldThread, oldThread.getName());

Don't try this at home

Thursday, March 13, 2008

StringBuilder vs StringBuffer vs String.conca

StringBuilder vs StringBuffer vs String.concat - done right

illustration
How long is a piece of String?

Introduction

Concatenation of Strings is very easy in Java - all you need is a '+'. It can't get any easier than that, right? Unfortunately there are a few pitfalls. One thing you should remember from your first Java lessons is a small albeit important detail: String objects are immutable. Once constructed they cannot be changed anymore.

Whenever you "change" the value of a String you create a new object and make that variable reference this new object. Appending a String to another existing one is the same kind of deal: a new String containing the stuff from both is created and the old one is dropped.

You might wonder why Strings are immutable in first place. There are two very compelling reasons for it:

  1. Immutable basic types makes things easier. If you pass a String to a function you can be sure that its value won't change.
  2. Security. With mutable Strings one could bypass security checks by changing the value right after the check. (Same thing as the first point, really.)

The performance impact of String.concat()

Each time you append something via '+' (String.concat()) a new String is created, the old stuff is copied, the new stuff is appended, and the old String is thrown away. The bigger the String gets the longer it takes - there is more to copy and more garbage is produced.

Creating a String with a length of 65536 (character by character) already takes about 22 seconds on an AMD64 X2 4200+. The following diagram illustrates the exponentially growing amount of required time:

String.concat() - exponential growth
Figure 1: StringBuilder vs StringBuffer vs String.concat

StringBuilder and StringBuffer are also shown, but at this scale they are right onto the x-axis. As you can see String.concat() is slow. Amazingly slow in fact. It's so bad that the guys over at FindBugs added a detector for String.concat inside loops to their static code analysis tool.

When to use '+'

Using the '+' operator for concatenation isn't bad per se though. It's very readable and it doesn't necessarily affect performance. Let's take a look at the kind of situations where you should use '+'.

a) Multi-line Strings:

String text=
"line 1\n"+
"line 2\n"+
"line 3";

Since Java doesn't feature a proper multi-line String construct like other languages, this kind of pattern is often used. If you really have to you can embed massive blocks of text this way and there are no downsides at all. The compiler creates a single String out of this mess and no concatenation happens at runtime.

b) Short messages and the like:

System.out.println("x:"+x+" y:"+y);

The compiler transforms this to:

System.out.println((new StringBuilder()).append("x:").append(x).append(" y:").append(y).toString());

Looks pretty silly, doesn't it? Well, it's great that you don't have to write that kind of code yourself. ;)

If you're interested in byte code generation: Accordingly to Arno Unkrig (the amazing dude behind Janino) the optimal strategy is to use String.concat() for 2 or 3 operands, and StringBuilder for 4 or more operands (if available - otherwise StringBuffer). Sun's compiler always uses StringBuilder/StringBuffer though. Well, the difference is pretty negligible.

When to use StringBuilder and StringBuffer

This one is easy to remember: use 'em whenever you assembe a String in a loop. If it's a short piece of example code, a test program, or something completely unimportant you won't necessarily need that though. Just keep in mind that '+' isn't always a good idea.

StringBuilder and StringBuffer compared

StringBuilder is rather new - it was introduced with 1.5. Unlike StringBuffer it isn't synchronized, which makes it a tad faster:

StringBuilder compared with StringBuffer
Figure 2: StringBuilder vs StringBuffer

As you can see the graphs are sort of straight with a few bumps here and there caused by re-allocation. Also StringBuilder is indeed quite a bit faster. Use that one if you can.

Initial capacity

Both - StringBuilder and StringBuffer - allow you to specify the initial capacity in the constructor. Of course this was also a thing I had to experiment with. Creating a 0.5mb String 50 times with different initial capacities:

different initial capacities compared
Figure 3: StringBuilder and StringBuffer with different initial capacities

The step size was 8 and the default capacity is 16. So, the default is the third dot. 16 chars is pretty small and as you can see it's a very sensible default value.

If you take a closer look you can also see that there is some kind of rhythm: the best initial capacities (local optimum) are always a power of two. And the worst results are always just before the next power of two. The perfect results are of course achieved if the required size is used from the very beginning (shown as dashed lines in the diagram) and no resizing happens at all.

Some insight

That "PoT beat" is of course specific to Sun's implementations of StringBuilder and StringBuffer. Other implementations may show a slightly different behavior. However, if these particular implementations are taken as target one can derive two golden rules from these results:

  1. If you set the capacity use a power of two value.
  2. Do not use the String/CharSequence constructors ever. They set the capacity to the length of the given String/CharSequence + 16, which can be virtually anything.

Benchmarking method

In order to get meaningful results I took care of a few things:

  • VM warmup
  • separate runs for each test
  • each sample is the median of 5 runs
  • inner loops were inside of each bench unit

The messy code is also available.

Tuesday, March 11, 2008

Understanding Weak References

Posted by enicholas on May 04, 2006 at 05:06 PM

Some time ago I was interviewing candidates for a Senior Java Engineer position. Among the many questions I asked was "What can you tell me about weak references?" I wasn't expecting a detailed technical treatise on the subject. I would probably have been satisfied with "Umm... don't they have something to do with garbage collection?" I was instead surprised to find that out of twenty-odd engineers, all of whom had at least five years of Java experience and good qualifications, only two of them even knew that weak references existed, and only one of those two had actual useful knowledge about them. I even explained a bit about them, to see if I got an "Oh yeah" from anybody -- nope. I'm not sure why this knowledge is (evidently) uncommon, as weak references are a massively useful feature which have been around since Java 1.2 was released, over seven years ago.

Now, I'm not suggesting you need to be a weak reference expert to qualify as a decent Java engineer. But I humbly submit that you should at least know what they are -- otherwise how will you know when you should be using them? Since they seem to be a little-known feature, here is a brief overview of what weak references are, how to use them, and when to use them.

Strong references

First I need to start with a refresher on strong references. A strong reference is an ordinary Java reference, the kind you use every day. For example, the code:

StringBuffer buffer = new StringBuffer();

creates a new StringBuffer() and stores a strong reference to it in the variable buffer. Yes, yes, this is kiddie stuff, but bear with me. The important part about strong references -- the part that makes them "strong" -- is how they interact with the garbage collector. Specifically, if an object is reachable via a chain of strong references (strongly reachable), it is not eligible for garbage collection. As you don't want the garbage collector destroying objects you're working on, this is normally exactly what you want.

When strong references are too strong

It's not uncommon for an application to use classes that it can't reasonably extend. The class might simply be marked final, or it could be something more complicated, such as an interface returned by a factory method backed by an unknown (and possibly even unknowable) number of concrete implementations. Suppose you have to use a class Widget and, for whatever reason, it isn't possible or practical to extend Widget to add new functionality.

What happens when you need to keep track of extra information about the object? In this case, suppose we find ourselves needing to keep track of each Widget's serial number, but the Widget class doesn't actually have a serial number property -- and because Widget isn't extensible, we can't add one. No problem at all, that's what HashMaps are for:

serialNumberMap.put(widget, widgetSerialNumber);

This might look okay on the surface, but the strong reference to widget will almost certainly cause problems. We have to know (with 100% certainty) when a particular Widget's serial number is no longer needed, so we can remove its entry from the map. Otherwise we're going to have a memory leak (if we don't remove Widgets when we should) or we're going to inexplicably find ourselves missing serial numbers (if we remove Widgets that we're still using). If these problems sound familiar, they should: they are exactly the problems that users of non-garbage-collected languages face when trying to manage memory, and we're not supposed to have to worry about this in a more civilized language like Java.

Another common problem with strong references is caching, particular with very large structures like images. Suppose you have an application which has to work with user-supplied images, like the web site design tool I work on. Naturally you want to cache these images, because loading them from disk is very expensive and you want to avoid the possibility of having two copies of the (potentially gigantic) image in memory at once.

Because an image cache is supposed to prevent us from reloading images when we don't absolutely need to, you will quickly realize that the cache should always contain a reference to any image which is already in memory. With ordinary strong references, though, that reference itself will force the image to remain in memory, which requires you (just as above) to somehow determine when the image is no longer needed in memory and remove it from the cache, so that it becomes eligible for garbage collection. Once again you are forced to duplicate the behavior of the garbage collector and manually determine whether or not an object should be in memory.

Weak references

A weak reference, simply put, is a reference that isn't strong enough to force an object to remain in memory. Weak references allow you to leverage the garbage collector's ability to determine reachability for you, so you don't have to do it yourself. You create a weak reference like this:

WeakReference weakWidget = new WeakReference(widget);

and then elsewhere in the code you can use weakWidget.get() to get the actual Widget object. Of course the weak reference isn't strong enough to prevent garbage collection, so you may find (if there are no strong references to the widget) that weakWidget.get() suddenly starts returning null.

To solve the "widget serial number" problem above, the easiest thing to do is use the built-in WeakHashMap class. WeakHashMap works exactly like HashMap, except that the keys (not the values!) are referred to using weak references. If a WeakHashMap key becomes garbage, its entry is removed automatically. This avoids the pitfalls I described and requires no changes other than the switch from HashMap to a WeakHashMap. If you're following the standard convention of referring to your maps via the Map interface, no other code needs to even be aware of the change.

Reference queues

Once a WeakReference starts returning null, the object it pointed to has become garbage and the WeakReference object is pretty much useless. This generally means that some sort of cleanup is required; WeakHashMap, for example, has to remove such defunct entries to avoid holding onto an ever-increasing number of dead WeakReferences.

The ReferenceQueue class makes it easy to keep track of dead references. If you pass a ReferenceQueue into a weak reference's constructor, the reference object will be automatically inserted into the reference queue when the object to which it pointed becomes garbage. You can then, at some regular interval, process the ReferenceQueue and perform whatever cleanup is needed for dead references.

Different degrees of weakness

Up to this point I've just been referring to "weak references", but there are actually four different degrees of reference strength: strong, soft, weak, and phantom, in order from strongest to weakest. We've already discussed strong and weak references, so let's take a look at the other two.

Soft references

A soft reference is exactly like a weak reference, except that it is less eager to throw away the object to which it refers. An object which is only weakly reachable (the strongest references to it are WeakReferences) will be discarded at the next garbage collection cycle, but an object which is softly reachable will generally stick around for a while.

SoftReferences aren't required to behave any differently than WeakReferences, but in practice softly reachable objects are generally retained as long as memory is in plentiful supply. This makes them an excellent foundation for a cache, such as the image cache described above, since you can let the garbage collector worry about both how reachable the objects are (a strongly reachable object will never be removed from the cache) and how badly it needs the memory they are consuming.

Phantom references

A phantom reference is quite different than either SoftReference or WeakReference. Its grip on its object is so tenuous that you can't even retrieve the object -- its get() method always returns null. The only use for such a reference is keeping track of when it gets enqueued into a ReferenceQueue, as at that point you know the object to which it pointed is dead. How is that different from WeakReference, though?

The difference is in exactly when the enqueuing happens. WeakReferences are enqueued as soon as the object to which they point becomes weakly reachable. This is before finalization or garbage collection has actually happened; in theory the object could even be "resurrected" by an unorthodox finalize() method, but the WeakReference would remain dead. PhantomReferences are enqueued only when the object is physically removed from memory, and the get() method always returns null specifically to prevent you from being able to "resurrect" an almost-dead object.

What good are PhantomReferences? I'm only aware of two serious cases for them: first, they allow you to determine exactly when an object was removed from memory. They are in fact the only way to determine that. This isn't generally that useful, but might come in handy in certain very specific circumstances like manipulating large images: if you know for sure that an image should be garbage collected, you can wait until it actually is before attempting to load the next image, and therefore make the dreaded OutOfMemoryError less likely.

Second, PhantomReferences avoid a fundamental problem with finalization: finalize() methods can "resurrect" objects by creating new strong references to them. So what, you say? Well, the problem is that an object which overrides finalize() must now be determined to be garbage in at least two separate garbage collection cycles in order to be collected. When the first cycle determines that it is garbage, it becomes eligible for finalization. Because of the (slim, but unfortunately real) possibility that the object was "resurrected" during finalization, the garbage collector has to run again before the object can actually be removed. And because finalization might not have happened in a timely fashion, an arbitrary number of garbage collection cycles might have happened while the object was waiting for finalization. This can mean serious delays in actually cleaning up garbage objects, and is why you can get OutOfMemoryErrors even when most of the heap is garbage.

With PhantomReference, this situation is impossible -- when a PhantomReference is enqueued, there is absolutely no way to get a pointer to the now-dead object (which is good, because it isn't in memory any longer). Because PhantomReference cannot be used to resurrect an object, the object can be instantly cleaned up during the first garbage collection cycle in which it is found to be phantomly reachable. You can then dispose whatever resources you need to at your convenience.

Arguably, the finalize() method should never have been provided in the first place. PhantomReferences are definitely safer and more efficient to use, and eliminating finalize() would have made parts of the VM considerably simpler. But, they're also more work to implement, so I confess to still using finalize() most of the time. The good news is that at least you have a choice.

Conclusion

I'm sure some of you are grumbling by now, as I'm talking about an API which is nearly a decade old and haven't said anything which hasn't been said before. While that's certainly true, in my experience many Java programmers really don't know very much (if anything) about weak references, and I felt that a refresher course was needed. Hopefully you at least learned a little something from this review.

All about intern()

Posted by enicholas on June 26, 2006 at 02:16 PM

Strings are a fundamental part of any modern programming language, every bit as important as numbers. So you'd think that Java programmers would go out of their way to have a solid understanding of them -- and sadly, that isn't always the case.

I was going through the source code to Xerces (the XML parser included in Java) today, when I found a very surprising line:

com.sun.org.apache.xerces.internal.impl.XMLScanner:395
protected final static String fVersionSymbol = "version".intern();

There are a number of strings defined like this, and every one of them is being interned. So what exactly is intern()? Well, as you no doubt know, there are two different ways to compare objects in Java. You can use the == operator, or you can use the equals() method. The == operator compares whether two references point to the same object, whereas the equals() method compares whether two objects contain the same data.

One of the first lessons you learn in Java is that you should usually use equals(), not ==, to compare two strings. If you compare, say, new String("Hello") == new String("Hello"), you will in fact receive false, because they are two different string instances. If you use equals() instead, you will receive true, just as you'd expect. Unfortunately, the equals() method can be fairly slow, as it involves a character-by-character comparison of the strings.

Since the == method compares identity, all it has to do is compare two pointers to see if they are the same, and obviously it will be much faster than equals(). So if you're going to be comparing the same strings repeatedly, you can get a significant performance advantage by reducing it to an identity comparison rather than an equality comparison. The basic algorithm is:

1) Create a hash set of Strings
2) Check to see if the String you're dealing with is already in the set
3) If so, return the one from the set
4) Otherwise, add this string to the set and return it

After following this algorithm, you are guaranteed that if two strings contain the same characters, they are also the same instance. This means that you can safely compare strings using == rather than equals(), gaining a significant performance advantage with repeated comparisons.

Fortunately, Java already includes an implementation of the algorithm above. It's the intern() method on java.lang.String. new String("Hello").intern() == new String("Hello").intern() returns true, whereas without the intern() calls it returns false.

So why was I so surprised to see protected final static String fVersionSymbol = "version".intern(); in the Xerces source code? Obviously this string will be used for many comparisons, doesn't it make sense to intern it?

Sure it does. That's why Java already does it. All constant strings that appear in a class are automatically interned. This includes both your own constants (like the above "version" string) as well as other strings that are part of the class file format -- class names, method and field signatures, and so forth. It even extends to constant string expressions: "Hel" + "lo" is processed by javac exactly the same as "Hello", and "Hel" + "lo" == "Hello" will return true.

So the result of calling intern() on a constant string like "version" is by definition going to be the exact same string you passed in. "version" == "version".intern(), always. You only need to intern strings when they are not constants, and you want to be able to quickly compare them to other interned strings.

There can also be a memory advantage to interning strings -- you only keep one copy of the string's characters in memory, no matter how many times you refer to it. That's the main reason why class file constant strings are interned: think about how many classes refer to (say) java.lang.Object. The name of the class java.lang.Object has to appear in every single one of those classes, but thanks to the magic of intern(), it only appears in memory once.

The bottom line? intern() is a useful method and can make life easier -- but make sure that you're using it responsibly.

Monday, March 10, 2008

Load Balancing

February 2008

Discussion

http://www.theserverside.com/tt/knowledgecenter/knowledgecenter.tss?l=LoadBalancingTomcatApache

Introduction

Tomcat is a popular application server used to host web applications. Apache is a popular web server which provides services like https encryption and decryption, URL rewriting etc. Apache can also be used a load balancer to balance load between several Tomcat application servers.

This article briefly discusses some alternatives for load balancing an application server. It discusses implementation details for setting up load balancing with Apache using ‘mod_proxy’ module. It also looks at some of the features provided by apache such as ‘server affinity’ and safe removal of node.

Downloadable web application is provided which can be used to test load balancing with Apache. Jmeter script is also provided for load testing Apache.

Setting up Apache and load balancing is the role of the System Administrator. Unless a java developer works in a small team or is setting up a test environment, he wont get involved in setting up load balancing. However, it is good to understand the principles behind load balancing. The knowledge might help a developer to fix issues in live environment.

This article discusses load balancing in the context of a web application. The application is accessed using https, requires a user to login and some user specific information is stored in session.

Background knowledge

It is assumed that the reader is familiar with the following concepts and technologies:

  • Apache (Version 2.2)
  • Tomcat (version 5.5.23)
  • Jmeter for load testing web applications
Some important terms

Load balancing – user requests are processed by more than one server with all servers sharing the load ‘equally’
Server affinity (sticky session) – With server affinity, multiple requests from a user are processed by the same server. This is required for non clustered servers as the user session data is held on one server only and all requests from that user have to go to the server which has the session data for the user.
Transparent failover – User is not aware of a server crash. Transparent failover can be request level or session level. Transparent failover can be achieved by clustering application servers. With clustering, all the servers are the same and so the loss of a server does not interrupt the service. With load balancing alone, the user has to login again when server crashes.
Server Cluster – A group of servers which appear to be a single server to the user. Loss of a server is transparent to the user. User data is held on all servers in the cluster group. Any server can process any user request and loss of any server does not lead to interruption of service. The user does not have to login after a server failure. Since the user session data is replicated over the network to more than one server, there is a performance overhead. So clustering should be avoided unless transparent failover is required.
Scalability – measure of the ability of a system to handle increasing load without reducing response time
Response time – time taken to process a user request
Real workers – Term used by apache to refer to servers which are components of a load balanced system. The real workers do the actual work and the real worker is usually a remote host.
Virtual worker – In Apache, the load balancer is referred to as the virtual worker which delegates processing to real workers.

Load balancing algorithms

  • Round robin – requests are Reduced likelihood of version conflicts
  • Weighted round robin – servers of different capacity are assigned requests in proportion to their capacity (as defined by a load factor). Apache as 2 versions of this:
    • Request counting algorithm – requests are delegated in round robin manner irrespective of the nature of the request
    • Weighted traffic counting algorithm – Apache delegates traffic to real worker based on the number of bytes in the request

Compare Load balancing with Clustering

Both Load balancing and clustering aim to improve scalability by spreading load over more than one server. They both aim to provide horizontal scalability.

Load balancing

Clustering

User has to login after server crash

User does not have to login after server crash. So failover is transparent to the user

Load balancing is done by the web server or using DNS or using hardware load balancer or using Tomcat balancer web application

Clustering capability is provided by the application server. Clustering also requires load balancing

The application servers (e.g. Tomcat) do not communicate with each other

The application servers (e.g. Tomcat) communicate with each other.

There is minimal effect on response time when moving up from a single server to load balanced servers under the same load

Response times could deteriorate when moving to a clustered system from a single server as the session data is now replicated over the network to other servers.

More the session data, more is the deterioration in performance compared to a single server.

Response time also depends on number of nodes in the cluster. More the number of nodes, more is the deterioration in performance as data is replicated using TCP to every single node in cluster. This can be reduced by using UDP to replicate session data. With UDP, session data could be lost during session replication. So a user might have to login again if a server crashes

Load balancing can be used independently of clustering

Clustering also requires load balancing but makes ‘server affinity’ redundant. It provides ‘transparent failover’ capability over load balancing at cost of decreased response time and more complex configuration

Usually no changes are required to move an application from a single server to a load balanced set of servers

Application must meet certain criteria for it to work in a clustered environment. The user variables stored in the session must be ‘serializable’. To get good response time, only small objects must be stored in the session.

Choices for implementing Load balancing

Hardware based load balancing

  • Pros
    • Fast
  • Cons
    • Expensive
    • Proprietary
    • Less flexible

Software based load balancing (e.g. Apache or Tomcat balancer)

  • Pros
    • Open source and free to implement with Apache and Tomcat balancer application
    • Easy to configure
    • More flexib
  • Cons
    • Lower performance compared to hardware based solution

Alternatives for software based load balancing

  • Apache ‘mod_proxy’ module or ‘mod_jk’ module. However ‘mod_proxy’ is easier to configure and newer than ‘mod-jk’ module.
  • Using Tomcat balancer application
  • Linux virtual server
  • Using DNS for load balancing

The rest of this article discusses load balancing using Apache ‘mod_proxy’ module.

Load balancing with server affinity

A simple load balanced setup which does not provide ‘server affinity’ is not suitable for stateful web applications. In stateful web applications, user state (session data) is held on one server. All further requests from that user must be processed by the same server. Hence server affinity (sticky sessions) is necessary for stateful web applications which don’t use clustering. The minimum load balanced setup is with 2 application servers and one web server (load balancer). If https decryption is required, then the same Apache server can also be used for https decryption.In a load balanced system with server affinity, all requests from user 1 go to Tomcat instance 1. This is shown below:

Apache implements server affinity by rewriting the ‘jsessionid’ sent by Tomcat to the browser. The Tomcat worker name is added to the end of ‘jsessionid’ before the ‘jsessionid’ is sent to the browser. In the next request from the same user, the Tomcat worker name is read from the ‘jsessionid’ and the request delegated to this Tomcat real worker.The ‘jsessionid’ stored as a cookie in the browser has the tomcat worker name as shown in screenshot below:

The Apache configuration (in httpd.conf) to setup Apache as a load balancer for 2 application servers is shown below:

ProxyPass /apache-load-balancing-1.0 balancer://mycluster stickysession=JSESSIONID


BalancerMember ajp://tomcat1:8009/apache-load-balancing-1.0 route=tomcat1 loadfactor=50
BalancerMember ajp://tomcat2:8009/apache-load-balancing-1.0 route=tomcat2 loadfactor=50

The above setup requires ‘mod_proxy’ module to be loaded.The first line sets up a reverse proxy for request ‘/apache-load-balancing-1.0’ and delegates all requests to load balancer (virtual worker) with name ‘mycluster’. Sticky sessions are enabled and implemented using cookie ‘JSESSIONID’.The ‘Proxy’ section lists all the servers (real workers) which the load balancer can use. For each participant in load balancing, we define the url (using http, ftp or ajp protocol) and give it a name which matches the name of the Tomcat engine defined in the Tomcat ‘server.xml’. The ‘loadfactor’ can be a number between 1 and 100. Set it to 50 so it can be increased or decreased dynamically later on.The tomcat ‘server.xml’ in ‘TOMCAT_FOLDER/conf’ folder should have this configuration:
....

...

With 2 browser sessions, the Tomcat server console and the server response show that all requests from the same user are processed by the same server.

Load balancing manager and safe removal of server node

Apache load balancing manager Apache includes a ‘balancer manager’ which can be used to check the status of the servers used for load balancing and to prepare for safe removal of a node. To enable balancer manager, add this section to Apache ‘httpd.conf’ file:

SetHandler balancer-manager

Balancer manager requires ‘mod_proxy’ and ‘mod_proxy_balancer’ modules to be loaded.

Balancer manager will now be accessible at the url ‘/balancer-manager’.

Screenshot of this is shown below:

Safe removal of a server node When an application server (real worker) has to be taken offline for maintenance, set a very low load factor for the server due to be taken offline. Few new users will be served by that server. All existing users ‘sticking’ to this real worker will continue to be processed by this real worker. This will help reduce the number of users who will have to login again when the real worker is taken offline.After some time, the real worker is disabled and taken offline. Once it is ready to come online, the worker is enabled again in balancer manager.

Tests

About the attached web application and JMeter scriptThe attached web application contains a single servlet and 3 JSP pages. The servlet has hard coded usernames and passwords for 10 users and is used to authenticate the user and store the username in the session. The servlet also prints the username and server name of server. The login JSP (index.jsp) is used to login and the second JSP (greeting.jsp) is used to print a greeting after the user logs in. This JSP also prints the username and server name in the response. The third JSP (user_details.jsp) is used to print user details and the server name and IP address of the server name used to process the request. This JSP also prints the username and server name of the server to the console. The name of the server is setup as a context parameter in the web.xml. Change it before deploying it so that the 2 web applications have different names to make it easier to identify them
 
serverName
Tomcat instance 1

The attached JMeter script sets up 10 threads with each thread being used to login 1 user and request user details. Login is done once per thread/user and all subsequent requests from the user are requests to get user details.Manual test with 2 browser windows to show server affinityOpen 2 browser windows and login using different usernames. The server consoles in Tomcat will show that requests from one user will always go to a particular Tomcat instance demonstrating server affinity. See screenshot below:

Load testing with JMeterThe attached JMeter script is used to simulate a load of 10 users. The attached JMeter script has been setup to record the response. Comparing the response from the server and the server console, we can see that the load of 10 users is shared equally between 2 servers and all requests from one user are processed by the same server.

Sudden loss of one server and then restoring the serverThis can be simulated with either shutting down tomcat to simulate a server crash. This is best simulated using JMeter as a client to simulate a load of 10 users continuously requesting pages from the servers.Run the attached JMeter script and once the load test is running, take one server down. Any subsequent requests to the offline server will be redirected to the second server. When one server goes down, the user session data is lost and so all the users who have ‘affinity’ to that server will have to login again.

Reducing single point of failure

Load balancer can become the single point of failure. This can be reduced by using round robin DNS to delegate user requests to more than one load balancer. The load balancer delegates requests to more than one application server. In this scenario, if the load balancer and/or the application server goes down, the other load balancer and application servers can still provide some level of service. This is illustrated in the diagram below

:

Conclusion

Apache can be used to load balance Tomcat servers. This setup provides other useful features such as ‘Server affinity’ and safe removal of nodes for scheduled maintenance. Load balancing is recommended if transparent failover is not required. It is easy to setup load balancing and ‘server affinity’ with Apache.JMeter can be used to load test the configuration and to test the behaviour in case of a server crash.The load balancer can become the single point of failure. This can be reduced by using 2 load balancer and using round robin DNS to delegate request to more than one server.

Source Files

web application.zip
apache load balance load test script.jmx

Biography

Avneet Mangat 6 years experience in Java/J2EE. Currently working as Lead developer at Active Health Partners ( www.ahp.co.uk ). Bachelors degree in Software Engineering, Sun Certified Web developer and Java programmer, Adobe certified Flash Designer and Prince2 certified (foundation). Lead developer of open source tool DBBrowser, please see http://databasebrowser.sourceforge.net/ Outside interests include photography and travelling. Please contact me at avneet.mangat@ahp.co.uk or avneet.mangat@gmail.com for more information.


PRINTER FRIENDLY VERSION