Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Attack of the clones

Time and space considerations in four different approaches to implementing deep clone() methods

  • Print
  • Feedback

Page 2 of 7

In a previous Java Q&A article, I developed a simple timing library that comes in handy now. This code in class Main measures the cost of TestClass.clone():

        // Create an ITimer:
        final ITimer timer = TimerFactory.newTimer ();
        
        // JIT/hotspot warmup:
        // ...
        TestClass obj = new TestClass ();
        
        // Warm up clone():
        // ...
        final int repeats = 1000;
        
        timer.start ();
        // Note: the loop is unrolled 10 times
        for (int i = 0; i < repeats / 10; ++ i)
        {
            obj = (TestClass) obj.clone ();
            ... repeated 10 times ...
        }
        timer.stop ();
        
        final DecimalFormat format = new DecimalFormat ();
        format.setMinimumFractionDigits (3);
        format.setMaximumFractionDigits (3);
        
        System.out.println ("method duration: " +
            format.format (timer.getDuration () / repeats) + " ms");


I use the high-resolution timer supplied by TimerFactory with a loop that creates a moderate number of cloned objects. The elapsed time reading is reliable, and there is little interference from the garbage collector. Note how the obj variable continuously updates to avoid memory caching effects.

Also note how clone() is implemented in both classes. The implementation in each class is in fact four, selected one at a time using four conditional compilation constants in Main: OBJECT_CLONE, COPY_CONSTRUCTOR, SERIALIZATION, and REFLECTION. Recompile the entire object when changing the cloning approach.

Let's now examine each approach in detail.

Approach 1: Cloning by chaining to Object.clone()

This is perhaps the most classical approach. The steps involved are:

  • Declare your class to implement the Cloneable marker interface.
  • Provide a public clone override that always begins with a call to super.clone() followed by manual copying of all deep fields (i.e., mutable fields that are object references and cannot be shared between several instances of the parent class).
  • Declare your clone override not to throw any exceptions, including CloneNotSupportedException. To this effect, the clone() method in your hierarchy's first class that subclasses a non-Cloneable class will catch CloneNotSupportedException and wrap it into an InternalError.


Correct implementation of Cloneable easily deserves a separate article. Because my focus is on measuring performance, I will repeat the relevant points here and direct readers to existing references for further details (see Resources).

This traditional approach is particularly well suited to the presence of inheritance because the chain of super.clone() eventually calls the native java.lang.Object.clone() implementation. This is good for two reasons. First, this native method has the magic ability to always create an instance of the most derived class for the current object. That is, the result of super.clone() in TestBaseClass is an instance of TestClass when TestBaseClass.clone() is part of the chain of methods originating from TestClass.clone(). This makes it easy to implement the desirable x.clone().getClass() == x.getClass() invariant even in the presence of inheritance.

Second, if you examine the JVM sources, you will see that at the heart of java.lang.Object.clone() is the memcpy C function, usually implemented in very efficient assembly on a given platform; so I expect the method to act as a fast "bit-blasting" shallow clone implementation, replicating all shallow fields in one fell swoop. In many cases, the only remaining manual coding is done to deeply clone object reference fields that point to unshareable mutable objects.

  • Print
  • Feedback

Resources