Six Java features to stay away from

September 18, 2013 by Nikita Salnikov-Tarnovski

I have spent countless hours troubleshooting different applications. Via 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 the regular Java EE developers in mind, not to 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 the 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 introspecting a business code is something that is bad in 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));
	}
}

Then 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 more tricky than getting rejected by your build script.

And last, there will be overhead. Reflection calls are optimized differently by the JIT. Some optimizations take longer to apply and some even cannot be applied. So the performance penalties on reflection can, sometimes, be orders of magnitude. But on 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 you business code is via AOP. Other than this, you are better off staying away from the reflection.

Bytecode manipulation. If I see you are using CGLIB or ASM directly 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 feeling the pain.

What makes things worse here is that you do not have the executable code anywhere in sight during the compile-time. Essentially, you do not know what code is in fact running in your production. So when facing troubles 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 could start feeling the temptation to use the variables without explicitly passing them down through the method invocation chain. Which could be useful on certain occasions. But when 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. 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, the classloaders are a complex beasts. You must first understand them, the 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 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 do 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. I do know that being equipped with a hammer makes you look around for a nail.

Why I think caching is not a good nail for such a hammer here you might wonder. 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 constantly running.

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

Can't figure out what causes your OutOfMemoryError? Read more

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

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

Can't figure out what causes your OutOfMemoryError? Read more

Latest
Recommended
You cannot predict the way you die
When debugging a situation where systems are failing due to the lack of resources, you can no longer count on anything. Seemingly unrelated changes can trigger completely different messages and control flows within the JVM. Read more
Tuning GC - it does not have to be that hard
Solving GC pauses is a complex task. If you do not believe our words, check out the recent LinkedIn experience in garbage collection optimization. It is a complex and tedious task, so we are glad to report we have a whole lot simpler solution in mind Read more
Building a nirvana
We have invested a lot into our continuous integration / delivery infrastructure. As of now we can say that the Jenkins-orchestrated gang consisting of Ansible, Vagrant, Gradle, LiveRebel and TestNG is something an engineer can call a nirvana. Read more
Creative way to handle OutOfMemoryError
Wish to spend a day troubleshooting? Or make enemies among sysops? Registering pkill java to OutOfMemoryError events is one darn good way to achieve those goals. Read more