1

I am looking to refactor my application to allow Escape Analysis and Scalar Replacement to kick in for one particular object, that's allocated very often.
I assume I will not hit any inlining limits in JVM because I can just tune in case there is need (full inlining is one of the needs for Scalar Replacement to work)

Assuming a basic code flow like this:

public class DataHandler {
// IHandler is an interface of which there are 4 different implementations at runtime
  private IHandler handler; // child handler - implementation

  public void handleData(SomeData data) {
    HeavyObjectIWantToScalarReplace obj = new HeavyObjectIWantToScalarReplace(data);
    handler.handle(obj)
  }
}

Is invoking a virtual method with object enough to break scalar replacement with latest OpenJDK or OpenJ9? If so, what solution could be used that would be the closest to current OOP approach while avoiding invokevirtual?
Maybe invokevirtual can be optimized out by JIT and it is enough to make sure none of these 4 handler classes are "leaking" reference to HeavyObjectIWantToScalarReplace?

underflow
  • 73
  • 1
  • 7
  • As you correctly noticed, the key prerequisite to scalar replacement is full inlining. HotSpot JVM can inline at most two implementations of a virtual method at one call site. If you have 4 possible implementations, and all of them are actually called, this will mean that `obj` escapes, and no scalar replacement will happen. – apangin May 10 '21 at 20:15
  • As a workaround, you can split one call site into multiple ones: `if (handler is child1 or child2) { handler.handle(obj); } else { handler.handle(obj); }` Looks weird, but may work in HotSpot. Don't know about OpenJ9. – apangin May 10 '21 at 20:25
  • thanks, very useful information about "two implementations"! I will continue digging, as the needed refactor is much bigger than this example code, to test if scalar replacement can kick in. – underflow May 10 '21 at 20:47
  • @apangin, if I use your call site split, and one of the two handler implementations won't be inlined because they are used very very rarely in comparsion to main handler, is that a deal breaker for scalar replacement? if so, is there any workaround? – underflow May 11 '21 at 16:20
  • Inlining policy is [intricate](https://stackoverflow.com/a/36585729/3448419), it depends on many factors. It's not easy to predict whether a method will be inlined in a particular case - it's better to check the actual compiler decisions with `-XX:+PrintInlining`. – apangin May 12 '21 at 01:06
  • If some branch is never called - it is not a problem for JIT; this branch will not be compiled at all, there will be an [uncommon trap](https://openjdk.java.net/groups/hotspot/docs/HotSpotGlossary.html) instead. – apangin May 12 '21 at 01:09
  • [Recommended read](https://shipilev.net/blog/2015/black-magic-method-dispatch/) on how the method invocation works in HotSpot. – apangin May 12 '21 at 01:12
  • I did check the generated code with jitwatch and inlining of handlers was working, but on the deeper level it was still bailing out from inline no matter how hard I hit the JVM and how many limits I increase. I think it was hitting the hardcoded node count limits. I did use a few smart ideas to simplify control flow to only have the biggest functions inlined once with no need for repeat, but still no luck. I decided to go with ThreadLocal variables instead hoping for similar performance, as the code executes within EventLoop threads. – underflow May 12 '21 at 10:01
  • I also had a look at -XX:+PrintEscapeAnalysis logs but seems like there are no tools to analyze it, and I was unable to interpret them myself. – underflow May 12 '21 at 10:19

0 Answers0