Java's new vs newInstance()

Published on — Filed under protip

Edit: after an interesting discussion at StackOverflow, I’ve updated some values and the conclusion.

Everybody knows (or should) that any operation performed using reflections is bound to be slower than its static, compiled counterpart.

I was making the classic micro-benchmarking error of not warming up the VM, so here's the slightly modified version of the code:

public class TheCostOfReflection {

  public static void main(String[] args)
        throws Exception {
    int nObjects = 100;
    Object[] objects = new Object[nObjects];

    // warm up a bit
    int warmupLoops = Math.min(nObjects, 100);
    for (int i = 0; i < warmupLoops; i++) {
      testNewOperator(nObjects, objects);
      testNewInstance(nObjects, objects);
    }

    System.gc();

    // using new
    System.out.println("Testing 'new'...");
    long newTime = testNewOperator(nObjects, objects);
    System.out.println((newTime / 1000000f) + "ms to create " +
                       nObjects + " objs via 'new' (" +
                       (nObjects / (newTime / 1000000f)) +
                       " obj/ms)");
    System.gc();
    // using newInstance() on class
    System.out.println("Testing 'newInstance()'...");
    long niTime = testNewInstance(nObjects, objects);
    System.out.println((niTime / 1000000f) + "ms to create " +
                       nObjects + " objs via reflection (" +
                       (nObjects / (niTime / 1000000f)) +
                       " obj/ms)");
    // ratio
    System.out.println("'new' is " + (niTime / (float) newTime) +
                       "x faster than 'newInstance()'.");
  }

  private static long testNewInstance(int nObjects, Object[] objects)
        throws Exception {
    long start = System.nanoTime();
    for (int i = 0; i < nObjects; i++) {
      objects[i] = Object.class.newInstance();
    }
    return System.nanoTime() - start;
  }

  private static long testNewOperator(int nObjects, Object[] objects) {
    long start = System.nanoTime();
    for (int i = 0; i < nObjects; i++) {
      objects[i] = new Object();
    }
    return System.nanoTime() - start;
  }
}

Running this with -Xms512m -Xmx512m (to avoid GC interference) yields the following result:

Testing 'new'...
7.610116ms to create 1000000 objs via 'new' (131404.05 obj/ms)
Testing 'newInstance()'...
184.72641ms to create 1000000 objs via reflection (5413.41 obj/ms)
'new' is 24.273798x faster than 'newInstance()'.

Nearly 25x slower. If you lower the value of nObjects the difference gets smaller, though.

Testing 'new'...
0.002794ms to create 100 objs via 'new' (35790.98 obj/ms)
Testing 'newInstance()'...
0.021581ms to create 100 objs via reflection (4633.7056 obj/ms)
'new' is 7.7240515x faster than 'newInstance()'.

Adding a -server flag to the VM parameters did the trick, though.

Testing 'new'...
8.862299ms to create 1000000 objs via 'new' (112837.54 obj/ms)
Testing 'newInstance()'...
13.91287ms to create 1000000 objs via reflection (71875.88 obj/ms)
'new' is 1.5698942x faster than 'newInstance()'.

And for 100 object instantiations:

Testing 'new'...
0.002864ms to create 100 objs via 'new' (34916.20 objs/ms)
Testing 'newInstance()'...
0.006007ms to create 100 objs via reflection (16647.24 objs/ms)
'new' is 2.0974162x faster than 'newInstance()'.

When the JIT magic kicks in, you get some SERIOUS improvements.

Conclusion

Even though I got far better results after introducing the warmup phase, I still suggest you use the good ol' Factory pattern. Unless you're creating a few dozen instances in a non-critical zones of your code, the factory is the way to go, as newInstance() can never (?) get as fast as new.