Avoid creating unnecessary objects
1. You can sometimes avoid creating unnecessary objects by using static factory methods.
2. Objects should be reused when they are immutable.
3. Lazy initializations are NOT preferrable as it might complicate simple code unnecessarily.
4. Any stateless object need not be created multiple times. E.g. Adaptor classes are generally stateless and can be reused.
5. Java has a lot of classes equivalent to its primitives. E.g. Long class is similar to long primitive. As far as possible, try using primitives. Prefer using primitives to boxed primitives and watch out for unintentional autoboxing (conversion from primitive to boxed primitive).
Eliminate obsolete object references
1. Obsolete references are those references which wont be deferenced again. Such unintentional object references might miss garbage collection.
2. Make all the object references null once they become obsolete.
Places to watch out for memory leaks:
1. Whenever a class matches its own memory. E.g. Stacks.
2. Caches – nullify all the cached items once they become outdated. Make use of WeakHashMap which are designed to do exactly this. They will get cleared automatically when all the outside references have become null. Other classes which do the same are LinkedHashMap. Java.lang.ref provides more sophisticated caches.
3. Listeners and other callbacks – Listeners might register callbacks but may not deregister. The best way to ensure that callbacks are garbage collected is to store only week references.
Avoid Finalizers
1. Finalizers are unpredictable, often dangerous, and generally unnecessary and can lead to severe performance penality, erratic behaviour, and introduce issues in portability.
2. JVM doesn't provide any guarantee about when it will execute finalizers. In fact it doesnt guarantee its execution at all. So any critical code like freeing an expensive resource should never be done on finalizers.
3. System.gc and System.runFinalization might increase the probability of finalizer running, but it doesnt guarantee anything. System.runFinalizersOnExit guarantees the execution of finalizer but might be deprecated soon.
4. When you want to free a resource, provide an explicit terminate() method which the client can use to free the resource. In case the client doesnt call it, then fall back on finalizers.
5. Also call the terminate() method in the finally block, so that it is almost always guaranteed to work.
6. Finalizer chaining (calling the superclass's finalizer method) is not done automatically. So when you override a superclass implementation of finalizer method, you should explicitly call the finalizer of the superclass.
When to use finalizers
1. Finalizer can work as a fallback, but even in that case it is good to log an error as the resource hasnt terminated properly and the code bug needs to be fixed.
2. While dealing with native objects, you can use finalizers to release some critical resources. This is because JVM doesn't keep track of native objects and so they wont be garbage collected.
I have taken these points from the book "Effective Java" which I consider as a MUST READ book for every JAVA developer.
I disagree with 5th point in first section.
ReplyDeleteIdeally, we should use only wrapper classes instead of primitive types.
Pros:
1. You can store null in "Double" object,which is not possible in "double". For eg:If you are fetching a column of double datatype from database, possible values could be "null" and "0". If you use primitive data type, then you can't differentiate between null and 0.
Cons:
Manually make sure of auto boxing, or just follow the coding convention everywhere to use only wrapper classes.
There is a clear difference on when to use what.
ReplyDeletefor example, you will never think of using an object as an iterator variable (int i looks natural than Integer i). You are talking about a special case where null is possible as a value. However it might not be the case all the time.