Notes of a (Java) developer

Software developers Programmers

Memory errors in Java

Software developer Programmer

Memory in Java is largely made easy by garbage collection. This automatic management of memory does not prevent from any memory problem. Here are some basics elements I find useful to review while facing a memory error.

The memory is divided in two distinct spaces, even if this might change in Java 8.

  • The PermGen space contains classes definitions
  • The Heap space contains objects

When a memory problem occurs, an error is raised. This throwable contains information about the cause of the problem and the impacted memory space. Most of time, these informations are sufficient to help solve the problem.

The JVM options mentioned in this post are for HotSpot (Sun/Oracle implementation). For more informations, the official HotSpot tuning documentation is very helpful.

The space allocated for classes definitions is full. This can happen if the environment loads a large number of classes (common in Java EE environments). The allocated space can be configured via the JVM option -XX:PermSize=256m (for 256 megabytes).

In some situations, this saturation can be caused by a hot deployment. A workaround for such a problem is the JVM option  -XX:+CMSClassUnloadingEnabled. This clear all references to obsolete classes definitions. A very helpful option when processing multiple hot deployments in a Tomcat environment.

A blog post by Frank Kieviet explains a pattern that could also cause PermGen space errors.

The created objects stay in the Heap space while being referenced in the executed program. When an object is not referenced any more, it becomes available for release by the Garbage Collector (GC). Most of time, the GC acts when the available memory becomes low. This is highly configurable with many JVM options.

If there is no memory leak, the easy solution is to increase the available space.

All becomes complicated when there is a memory leak. The cause can be difficult to identify. Several tools help to identify the problem nature.

I personally generate an hprof file. This is done using JVM option -XX:+HeapDumpOnOutOfMemoryError. Another JVM option -XX:HeapDumpPath=... allows to specify the generated file location. The Eclipse MAT tools helps to visualize the generated file.

Another solution is to run your program with the Eclipse TPTP tools. But I prefer to avoid this option because it requires much resources to run (and was quite unstable the last time I used it).

This error happens when the system spends too much time executing garbage collection. Literally, there is no memory saturation but the system uses most of its memory despite garbage collections. A simple workaround can be to extend the Heap space. If the problem persists, tuning the GC configuration will probably be the solution. A hint can be the -XX:-UseParallelGC JVM option (some documentation is available here).

Design by contract, assertions and exceptions

Software developer Programmer

Understanding design by contract is, I think, important for software quality in OOP because the principles are clear and efficient.

introduced the idea in 1988. It takes advantage of assertions as defined by (1969), the seminal work of an eponymous logic.

Assertions are associated to an object method and qualified in one of three categories :

  • Preconditions are assertions true before executing the method
  • Postconditions are assertions true after executing the method
  • Invariants are assertions true before and after executing the method

In practice...

includes his idea in the programming language : the assertions are checked in a static way (at compile time).

If the Java programming language provides support for assertions, these are checked only in a dynamic way (at runtime) and disabled by default. Because of runtime checking, exceptions are largely used instead of assertions. In Java, a proper use of exceptions can be more meaningful to the programmer than a proper use of assertions because it provides more details about the malfunction conditions (the stack trace). At first glance, assertions (in the design by contract way) seem to be very far from exceptions. This is to some extent a paradox: I think the developer should consider assertions concepts every time he uses exceptions.

In Effective Java , defines checked exceptions as recoverable conditions and runtime exceptions as programming errors.

In an non-Java context, says an exception is a situation where preconditions are satisfied but postconditions can not be satisfied.

I think that, to respect Bloch terms,

  • Runtime exceptions should be used for checking preconditions
  • Checked exceptions should be used when preconditions are true but postconditions can not be satisfied, assuming that the method is correct (e.g. the network connection is broken). 

If a runtime exception is raised, the calling method should be bugged (or its preconditions are not properly checked).

Some extensions to the Java platform provide support for static testing (assertions checking at compile time). For example, JML takes advantage of Java comments: this is interesting for traditional compiling compliance.

Java compilation with the Eclipse IDE and Tomcat

Software developer Programmer

You probably have noticed that the Eclipse IDE does not require a JDK to compile a Java program: this is possible with a simple JRE.

The IDE Java Development Tools (JDT) include the appropriate components to compile a program. An AST parser is exposed to help the code manipulation. Compilation is generally defined in two steps : analysis and synthesis. The AST is the analyzed version of  a program organized as a tree connecting declarations and definitions. A specific API called the Java model provides an abstraction layer for the AST. The C Development Tools (CDT) also include an AST parser.

With such informations, the IDE is in Java able to perform complex operations on the code design. Functions exposed in the refactor menu take advantage of the Eclipse AST parser.

The compilation performed using the Eclipse AST is reliable. Since tomcat 5.5, it is used by the Jasper engine to compile JSPs as servlets. This is why, since this version, the servlet container does not require any longer a JDK to run.

You should be careful when compiling with Eclipse : there are a few differences when compiling with the JDK (especially on generics transtyping).

References, caching and garbage collection

Software developer Programmer

For a long time I have been wondering how do frameworks manage memory while caching.

The EclipseLink documentation gave me the answer. The Java SE platform provide special references :

According to the official documentation, soft references are used to provide memory-sensitive properties to cached references. This can be very efficient.

But access to soft-referenced objects needs to be done via specific objects so that freed references can be renewed (e.g. via a persistent storage).

The official API also provides a special map with weak referenced keys. This class can be useful but is not really appropriate for caching as keys are released.

JAVA JPA IMPLEMENTATION

Dynamic assignation and proxy objects

Software developer Programmer

Still in JPA, you perhaps wonder how lazy-loading is managed. This is done via proxy objects.

The Java SE platform provides a proxy class which creates proxy objects for interfaces. But this does not help when you wish to do the job on POJOs.

A framework allows to do this (and many other bytecode operations) : Javassist. The ProxyFactory class allows to build a substitute object for any class via the createClass method. I guess the substitution is done by subclassing.

Instances of this "dynamic" class can be created calling the newInstance method. The created object implements the Proxy interface. To this this interface can be associated a MethodHandler object. Such an object is called on any access to the dynamic instance (including null tests). For a lazy-loading field, this is where to replace the dynamic instance.

It is also recommended to specify a MethodFilter object to the ProxyFactory to avoid the creation of proxies when finalizing the reference.

 	ProxyFactory factory = new ProxyFactory();
	factory.setSuperclass(MyClass.class);
	factory.setFilter(
	  new MethodFilter() {
	    public boolean isHandled(Method m) {
	        // The filter to avoid finalization
	        return !m.getName().equals("finalize");
	    }
	  }
	);
	Class cl = factory.createClass();
	MethodHandler handler = new MethodHandler() {
	    public Object invoke(
	      Object self, 
	      Method m, 
	      Method proceed,
	      Object[] args
	    ) throws Throwable {
	        // On each access, execution of the original operation
	        return proceed.invoke(self, args);
	    }
	};
	MyClass cls = (MyClass)cl.newInstance();
	((Proxy)cls).setHandler(handler);

My way crossed a single problem with Javassist: null tests. While using dynamic objects to avoid unnecessary database access, the method filter proceeded the proxy replacement (database access) properly on null tests in most cases. But in some rare cases, the null test did not call the method filter and did not perform the test properly. Perhaps was this a misuse of the library. Anyway the workaround was to perform a double check (null reference test + null test on a mandatory property) in order to ensure the method filter call.

Edit (2014-10-16) : this last point requires further explanations (in a few words this was a misuse). I used Javassist proxies to implement lazy-loading of an object-relational mapped attribute. The point was about replacing the proxy object. To do so, I was only listening to method calls on the proxy. But a field access is not a method call: it was possible to create direct references on the proxy object before replacing it. It seemed then impossible to replace all the assignated references and the proxy object was still assignated (that caused the null tests to fail).

JAVA PROXY JPA IMPLEMENTATION