Performance of Java 'final': methods calls on a final vs non-final class

As discussed on the first page, a common misconception about final is that it is useful as a performance optimisation. On this page, I will present some data illustrating that this isn't actually the case.

In the test, we take a class called BaseClass which defines a particular method, doOperaton(). Then, we create three types of classes that extend BaseClass, and override doOperation(), as follows:

We then consider calling a class from all three categories non-final (but not extended), final and extended, in one of two cases.

In the first case, the JVM cannot predict what what precise subclass of BaseClass the call is being made on. We define a method as follows:

private static int doTestRun(List<BaseClass> l) {
  int ret = 0;
  for (BaseClass c : l) {
    ret += c.doOperation();
  }
  return ret;
}

This method is then called with a list of instances whose precise class is chosen at random (e.g. FinalClassA or FinalClassB, although the classes in a given list are always of the same category (non-final, final or extended).

In the second case, the JVM can predict which precise subclass we make the call on. We define a method such as the following, where the precise subclass is specified in the method signature and so can be known by the compiler:

private static int doTestRunClassFinalA(List<ClassFinalA> l) {
  int ret = 0;
  for (ClassFinalA c : l) {
    ret += c.doOperation();
  }
  return ret;
}

We thus have six different tests, comprising the combinations of category (non-final, final, extended) and whether or not the precise subclass is known to the compiler.

On the next page, we look at the results of these tests, which show us the timings of final vs non-final and of knwon vs unknown subclass.