3

I have an class hierarchy rooted in an interface and implemented with an abstract base class. It something looks like this:

interface Shape {
  boolean checkFlag();
}

abstract class AbstractShape implements Shape {
  private boolean flag = false;

  protected AbstractShape() { /* compute flag value */ }

  public final boolean checkFlag() { return flag; }
}

interface HasSides extends Shape { 
  int numberOfSides();
}

interface HasFiniteArea extends Shape { 
  double area();
}

class Square extends AbstractShape implements HasSides, HasFiniteArea {

} 

class Circle extends AbstractShape implements HasFiniteArea { 
}

/** etc **/

When I sample the running code with VisualVM, it appears that AbstractShape.checkFlag() is never inlined and consumes 14% of total program running time, which is obscene for a method this simple, even for a method called so frequently.

I have marked the method final on the base class, and (currently) all classes implementing the "Shape" interface extend AbstractShape.

Am i interpreting the VisualVM sample results correctly? Is there any way to convince the JVM to inline this method or would I need to tear out the interfaces and just use an abstract base class? (I would prefer not to because the hierarchy includes interfaces like HasFiniteArea and HasSides which mean the hierachy does not have a perfect tree form)

EDIT: to be clear, this is a method that in any universe should be inlined. It is called more than 420 million times during a 2 minute execution and, because it is not inlined and remains a virtual call, it accounts for 14% of runtime. The question I'm asking is what is preventing the JVM from inlining this method and how do i fix it?

akbertram
  • 1,330
  • 10
  • 16

4 Answers4

5

Here is quote from the Wikipedia

A common misconception is that declaring a class or method final improves efficiency by allowing the compiler to directly insert the method inline wherever it is called. This is not completely true; the compiler is unable to do this because the classes are loaded at runtime and might not be the same version as the ones that were just compiled. Further, the runtime environment and JIT compiler have the information about exactly which classes have been loaded, and are able to make better decisions about when to inline, whether or not the method is final.

See also this article.

Oleg Pavliv
  • 20,462
  • 7
  • 59
  • 75
  • +1: and as add on to this answer: final methods can obviously not be inlined, if they are implemented in several different classes!! Look at it from the standpoint of the JVM, all you have is a `Shape` how the heck should the JIT compiler know if there is later a Rect or an Oval behind that interface, and hwo should it know which method to call/inline? – Angel O'Sphere Sep 27 '11 at 11:07
  • @Angel O'Sphere Actually the JIT is capable of figuring that out. It's one of those pesky de-optimizations. "Since all classes so far extend AbstractShape we can call final method X directly without virtual call overhead" (same applies theoretically to inlining too). As soon as a class is loaded that implements Shape but not AbstractShape this has to be deoptimized again. The hotspot VM is doing that at least for interfaces that are implemented by only a single class (since that's a much-used idiom in Java that helps with performance) – Voo Sep 27 '11 at 23:01
  • Ah, I see your final sentence ;D yeah I did not mention that as most people wont grasp it. If you look at my wording I explicitely asumed two implementations would be present ;D – Angel O'Sphere Sep 28 '11 at 12:17
  • @AngelO'Sphere The JVM does lots of wonderful optimizations. As Voo said, it does have enough information to know that all (loaded) implementations of the interface inherit from the same base class and share the same final implementation of the method. The question is whether the JVM **actually** does this or if I need to refactor my code in order to benefit from inlining. – akbertram Sep 29 '11 at 15:38
  • The current HotSpot JVM actually does it. I thought that was meanwhile clearly answered by everyone here ;D However the JVM only optimzes and inlines this method if it concludes it is worth it, e.g. it is called often in a loop. However (looking at your last answer) there might be variations in JITing depending on platform (SPARC/x86 etc.) – Angel O'Sphere Oct 04 '11 at 11:15
1

The default compiler threshold is 10000. -XX:CompilerThreshold= This means a method or a loop (for the server JVM) has to be called at least 10000 times before it is compiled to native code.

After it has been compiled it can be inlined, however the call stack does show this. It is smart enough to know the inlined code came from another method and you never see a truncated call stack.

profilers try sample code and assign time. It doesn't always do a great job and you get methods which are obvious not time consumers being assigned CPU time. VisualVM is a free profiler and it is implementing in Java. If you use a profiler like YourKit you can get more accurate results as it uses native code e.g. doesn't create garbage.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
1

After extensive experimentation, I could not get the Sun JDK 6 to inline this method when called on the interface.

Fortunately, there were a limited number of call sites involved, and changing

public void paint(Shape shape) {
  if(shape.checkFlag()) { /* do stuff */ }
} 

to

public void paint(Shape shape) {
  if(((AbstractShape)shape).checkFlag()) { /* .. */ }
}

is enough of a hint to get the JVM to inline the method. Running time of the calculation in question dropped 13% compared to the original runtime of 6 minutes.

akbertram
  • 1,330
  • 10
  • 16
  • That is the correct answer. It is not possible to inline methods on interfaces. You can enable `-XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining` and in the output you will see something like `Line 12345: @90 com.example.app::paint (3 bytes) not inlineable`. – kap Oct 27 '22 at 07:51
0

From the literature I've read on old versions of JVM's by declaring methods final it would determine it to transform that method inline. Now you don't have to specify that method to be final to optimize the code.

You should let the JVM optimize the code, and make the method final only if you explicitly don't want that method overridden. The JVM probably doesn't make your method inline because its own optimization is faster, considering the rest of the application's code.

Romeo
  • 337
  • 1
  • 6
  • 17