Splunk Completes Acquisition of Plumbr Learn more

To blog |

Six Java features to stay away from

September 18, 2013 by Nikita Salnikov-Tarnovski Filed under: Java

I have spent countless hours troubleshooting different applications. From the experience I can draw a conclusion about several Java SE features/APIs which most of the developers should just stay away from. When I refer to most of the developers, I have in mind regular Java EE developers, not the library designers / infrastructure engineers.

Full disclosure: I do honestly think that, in the long run, most of the teams are better off staying away from the following features. But as always there are exceptions. If you have a strong team and are fully aware of what you are doing, go ahead. In most cases though, if you start including following tools to your arsenal, you will regret it in the long run:

  • Reflection
  • Bytecode manipulation
  • ThreadLocals
  • Classloaders
  • Weak/Soft references
  • Sockets

But enough of the intro, let me go through the list of warning signs backed with the explanation of the underlying problems:

Reflection. In popular libraries such as Spring and Hibernate, the reflection has its place. But introspection in business code is something that is bad for so many reasons that I almost always recommend to avoid it:

First comes the code readability/tooling support. Open up your favourite IDE and find inter-dependencies in your Java code. Easy, isn’t it? Now, replace the method calls with reflection and try to repeat the process. Things go even more out of hand when you start modifying the state you should normally have encapsulated away. If you need an example, take a look at the following code:

public class Secret {
	private String secrecy;
	public Secret(String secrecy) {
		this.secrecy = secrecy;
	}
	public String getSecrecy() {
		return null;
	}
}

public class TestSecrecy {
	public static void main(String[] args) throws Exception {
		Secret s = new Secret("TOP SECRET");
		Field f = Secret.class.getDeclaredField("secrecy");
		f.setAccessible(true);
		System.out.println(f.get(s));
	}
}

You are about to miss compile-time safety. Seeing the same example above you can already see that making a typo in getDeclaredField() parameter is only discovered during runtime. As you might recall, discovering runtime bugs is a lot trickier than getting rejected by your build script.

And lastly, there will be overhead. Reflection calls are optimized differently by the JIT. Some optimizations take longer to apply and some cannot even be applied. So performance penalties on reflection can, sometimes, be in orders of magnitude. But in a typical business application you will not really notice the overhead, so this one is definitely a lesser of the evils.

To summarize, I can state that the only reasonable (indirect) reflection usage in your business code is via AOP. Other than that, you are better off staying away from reflection.

Bytecode manipulation. If I see you are using CGLIB or ASM directly in your Java EE application code, I feel like I immediately want to run away. Take the reasons I elaborated in reflection block, multiply the impact by five and you might start understanding the pain.

What makes things worse here is that you do not have the executable code anywhere in sight during compile-time. Essentially, you do not know what code is in fact running in your production. So when facing trouble you are thrown into runtime troubleshooting and debugging – which, if you agree with me, is a “bit” more time-consuming.

ThreadLocals. There are two unrelated reasons why seeing ThreadLocals in a business code makes me shiver. First, with the help of ThreadLocals, you might start feeling the temptation to use variables without explicitly passing them down through the method invocation chain which could be useful in certain occasions. But if you are not careful, I can guarantee that you will end up creating lots of unexpected dependencies within your code.

The second reason is related to my day-to-day work. Storing data in ThreadLocals builds a highway to memory leaks. At least one out of ten permgen leaks I face is caused by extensive ThreadLocal usage. When used in conjunction with the classloaders and thread pooling, the good old “java.lang.OutOfMemoryError:Permgen space” is just around the corner.

Classloaders. To start with, classloaders are complex beasts. You must first understand them, their hierarchy, delegation mechanics, class caching, etc. And even if you think you have gotten it, the first (ten?) attempts will still not work properly. At the minimum you will end up creating a classloader leak. So I can only recommend leaving this task to application servers.

Weak and Soft References. Just learned what they are and how they work? Good. Now you understand Java internals a bit better. Got that brilliant idea of rewriting all your caches with soft references? Not good. Although I do know that being equipped with a hammer makes you look around for a nail.

You might wonder why I think caching is not such a good nail for the hammer here. After all, building a cache upon soft references could be a good example of how to delegate some of the complexities to the GC instead of implementing it yourself.

Let us take an arbitrary cache as an example. You build it using soft references for values so that when memory is exhausted the GC can step in and start cleaning. But now you do not have control over which objects were removed from the cache and are more than likely to recreate them on next cache-miss. If memory is still scarce you trigger the GC to clean them again. I guess you can see the vicious circle forming and your app becoming CPU-bound with Full GC being run constantly.

Sockets. The plain-old java.net.Socket is just too tricky to get right. I do think it is fundamentally flawed due to its blocking nature. When writing a typical Java EE application with a web-based front-end you need a high degree of  concurrency to support your numerous users. What you now do not want to happen is to have your not-so-scalable-at-all thread pool sit there waiting for blocked sockets.

There are wonderful 3rd party libraries available for the task at hand though, so instead of trying to get things right yourself, go and grab Netty.

If you made it this far with the post then I can only guess I managed to write something you enjoyed. So subscribe to our Twitter feed to be alerted on the next posts on performance optimization and troubleshooting topics.

ADD COMMENT

Comments

Interesting

Vijay Ram

Thank you for the valuable post, interesting topics covered.

fuzzy

Stupid programmer..

linzuxiong

I was unaware of weak references but after going through your article i came to know about usefulness of it. I know you no very well about it but here is some basic information for the novice reader who definitely read your article.

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 returningnull.

Niel Modi

Yep, you’ve put it all right. Just to stress one thing: if there are not strong references to an object, GC will throw it away without looking at how many weak references are there. That if don’t bring soft and phantom references into the game 🙂 And we surely shouldn’t

iNikem

So obvious and so easy to forget, yes, programming a lib != programming business code… And for even many others examples like spring ioc wiring… Thanks to share your mind, it’s always a pleasure to see people struggling with same issues 😉

Jonathan Melly

Nice article, but IMHO a bit over the top. For example, ThreadLocals can be a nice way of always having NumberFormat instances ready in a threaded web application (and not create new instances on every request).

The bottom line should be: use your tools correctly – choose the right tool for every task.

Mathis

I agree with you, yes 🙂 But with ThreadLocals you essentially create your own custom scope and people often forget to clear the value when scope comes to end.

iNikem

In scala we have withValue:

myDynamicVariable.doWith(someValue) {
callSomething()
} // doWith wraps it in a try/finally

Naftoli Gugenheim

My advice to ‘regular’ developers would actually be to learn about these features and need be — study and understand them before jumping into writing / pasting code from random blogs…

Octavian

Nice article! Hope to see you guys again on Devoxx this year 🙂

Nocket

Thanks, always happy to publish content you enjoy. Devoxx is not yet decided as the talks have not been approved so far. I guess the only conference this year where we have not been accepted with a talk …

Ivo Mägi