About

Jadira is the home for Sousan and Chris Pheby's open source projects. These are reusable open source Java modules that provide first class solutions using the most effective current JEE technologies.

Search
Tag Cloud
...
Login
« Eclipse Kepler | Main | Eclipse Juno »
Tuesday
Jun252013

Announcing Jadira Cloning

Why Fast Cloning?

A recent discussion about low-latency design considerations had me considering the applicability of the various tried and tested design patterns to the low-latency context. Can we make use of Domain Driven Design, CQRS, Event Sourcing and other patterns in a low-latency context.

In practice, even high-performance Java is a good fit to these patterns. The same tools that drive standard Java solutions drive high performance solutions. In low latency applications we can exploit design approaches such as consolidating work to specific cores (possibly sharing related work between cores on the same CPU) in order to avoid contention and maximise the usage of memory cache; avoid locking and other sources of contention. Low latency libraries take these approaches to a logical conclusion, also making use of ByteBuffers and the sun.misc.Unsafe class to directly address memory.

In practice, we reserve these techniques to critical sections of applications, and more conventional techniques drive other parts of application build - this is essential ini order to build systems that can be maintained as well as quick. That isn't to say that we don't make use of low overhead libraries to integrate key capabilities. For example, a common issue in Acquire-Parse-Transform-Deliver workload is how to perform the transformation. In the past libraries such as Dozer were popular, but their performance is limited - today a performant alternative is Orika. Similarly Kryo provides an effective, general purpose serialization API. Parsing of large XML documents can deliver good throughput by combination of a StAX pull parser and JAXB. With these kind of approaches we can deliver acceptable performance to producer systems, whilst also achieving excellent maintenance outcomes.

How can a standard POJO domain entity populate an event stream? In future articles, I'll discuss how the domain entity can be rich (rather than anemic) in functionality, but for now, lets consider the scenario that when we manipulate the domain entity, we expect events to be fired. If we want to produce the events, we need to know how the entity has changed, but in the case of a POJO, we may not have this information. The way to preserve this information, is to copy (deep clone) the domain object. Enter Jadira Cloning, which provides fast cloning of almost any object. In (rough) tests on my laptop, Jadira cloning has been able to copy complex objects of around 120 bytes, around 700 000 times per second.

Introducing Jadira Cloning

Jadira Cloning is not the first library for deep cloning in Java. Rits Cloning is a well established library that offers much the same core functions as Jadira Cloning, but delivers around half to one-third of the throughput in the scenarios I tested. To make Jadira fast, a number of steps were taken. First, the introspection model is built once when the 'Cloner' instance is initialised. This reduces the runtime cost of cloning. An initialisation method allows this to be performed at startup, rather than on the first run. Second, the sun.misc.Unsafe class is used when available to perform memory access, with a fall back to a portable strategy when unavailable. For maximum Cloning performance, the server JVM should be used.

Jadira Cloning supports most of the features of Rits Cloning (discarding anonymous parent references is not included), but is designed to be extremely customisable and extensible. Jadira also provides a range of extensibility strategies that creates for a more versatile solution.

Using Cloners

Your basic mode of usage is:

Cloner cloner = new BasicCloner();

This defines a Cloner that uses Unsafe, where available, and a portable cloning strategy otherwise. The PortableCloningStrategy depends on the Objenesis library.

You can also select the strategy explicitly:

Cloner cloner = new BasicCloner(new UnsafeCloningStrategy());

Cloner cloner = new BasicCloner(new PortableCloningStrategy());

This approach makes it easy to customise the cloning behaviour... more on this later.

In addition to BasicCloner, a cut down implementation is provided. MinimalCloner is another implementation of Cloner that  simply hands off to Unsafe, and can be very fast - its most suitable for simple object structures. It is recommended for situations where only basic deep-cloning functionality is required and maximum throughput desirable. It is not always faster or appropriate, so consider your use case and be sure to benchmark. Perform clone with:

cloner.clone(obj);

The other method of Cloner, initialiseFor(Class<?> classes...) allows you to initialise a cloner at startup, rather than runtime.

Cloners are thread-safe and may be used as Singletons.

Customising BasicCloner

There are a variety of ways to customise cloning. The BasicCloner instance can be configured with CloneImplementors which can be implemented to override the normal cloning behaviour for particular classes. These are be enabled by setting useCloneImplementors() as needed. There are a variety of CloneImplementors built in for JDK collections and Calendar types (these have the same coverage as Rit's FastCloners).

CloneImplementor is subclassed by CloneDriver (concrete implementations are BasicCloner and MinimalCloner) and also by Clone strategy, so that are good opportunities for customising the clone strategy during a parse. This is particularly interesting when the @Cloneable annotation is used to customise the behaviour for a particular class.

Immutable and Transient Types

Specific classes can be indicated as being immutable and/or non-cloneable - these classes do not need to be copied during the clone. Cloning of immutables can be disabled or enabled using setCloneImmutables() whilst non-cloneable classes may never be cloned. Setting cloneTransientFields() to false will prevent the cloning of transient fields which will be set to their null (or default primitive) values instead of being cloned.

What about JDK's Cloneable

Using Cloneable is not recommended but there are classes out there that do. BasicCloner can detect classes that implement Cloneable and invoke their clone() method. To enable this function, set useCloneable().

Using Annotations for Customisation

Finally, the operation of the class can be customised using annotations. The @Cloneable annotation can be used to customise the treatment of particular classes being cloned. @Cloner can be used to specify a particular method within a class to be used to fulfil the clone for that specific class. @NonCloneable indicates that a class should not be cloned and the same instance returned. Finally the @Transient annotation can be used on any class field to indicate that the field is transient. In the case of this last annotation, use cloneTransientAnnotatedFields() to enable or disable the capability (by default these fields are not cloned).

@Immutable provides an alternative mechanism for indicating that a class is immutable, as opposed to configuring this directly on BasicCloner. In addition if the Mutability Detector library is on the classpath this will automatically be used to determine immutability for all classes being cloned. If a class is identified as IMMUTABLE or EFFECTIVELY_IMMUTABLE by Mutability Detector it will be treated as immutable.

Use of Recursion

Cloning naturally lends itself to recursion, and it turns out that this is the fastest, and most easily customisable way to traverse an object graph. As you recurse, you need to track what objects you have seen to preserve identity equality (an IdentityHashMap is used for this), but otherwise the process is relatively straightforward.

However, recursion is not suitable for very large object graphs or deeply linked structures (e.g. linked lists) as handling these results in StackOverflowException. Jadira Cloning guards for this eventuality by monitoring the size of the seen object graph, and switching to an iterative strategy if it grows too large. This approach means that even MinimalCloner can handle large graphs without encountering Stack Overflow.

Extra #1: for Objenesis...

The Portable Clone Strategy relies on Objenesis. In addition, for general use of Objenesis, an object construction strategy that can use Unsafe is provided.

Extra #2: for Orika...

For bean mapping with Orika, a mapping strategy that uses Jadira Cloning is provided. This is ideal for objects that simply need to be copied without further modification within your bean mappings. Orika's ASM based implementation means it adds little overhead over direct use of Jadira Cloning.

Get the Code

Find Jadira Cloning at the project site. Please report issues via the mailing list or Jira. Also, the artifacts are in Maven Central:

<dependency>
    <groupId>org.jadira.cloning</groupId>
    <artifactId>cloning</artifactId>
    <version>3.1.0.CR8</version>
</dependency>

Should Cloning be fixed in the JDK?

JDK 8 offers the potential for fixing Cloneable by adding a default clone() implementation to the interface that calls out to a deep cloning library... this won't impact existing implementors, but provides a way to fix what is currently one of the most broken and confusing of the JDKs idioms.

What's Next?

There are announcements coming for other areas of Jadira framework. In the meantime, in this module, I'm starting to think about implementing object diff, patch and merge, which builds nicely on this cloning capability.

PrintView Printer Friendly Version

EmailEmail Article to Friend

References (1)

References allow you to track sources for this article, as well as articles that were written in response to this article.

Reader Comments

There are no comments for this journal entry. To create a new comment, use the form below.

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>