Hacker News new | ask | show | jobs
by ncmncm 1966 days ago
This is about runtime binding vs static binding, not static objects or static members. There is still a "this", but the code that will run is known up front -- "ahead of time".

C++ went through this 25+ years ago: runtime binding, what in C++ is virtual functions, is a niche technique. Most C++ programs don't use it at all, or use it in only one or two spots. When it is the right thing, it makes the work convenient, but it really is just a dance with function pointers. In C++, templates do the heavy lifting.

Java never offered any other support for organizing programs, so inheritance and virtual functions have been your go-to for everything, no matter how bad the fit. In a static call there is only one bit of code to run, and it never changes over the life of the program. Just like almost everything, really, except here your runtime knows up front.

It was always a dumb choice to make member functions default to virtual semantics, when they almost always don't need it, and it just costs performance to no purpose. That is what comes out of treating language design as a marketing exercise: Java's designers really (and openly) cared less than nothing about object-oriented programming. They thought people really ought to be coding Lisp. Forcing runtime binding was a way to sneak in something a little bit lispy, and maybe get people used to production code running no faster than Lisp.

2 comments

> It was always a dumb choice to make member functions default to virtual semantics, when they almost always don't need it, and it just costs performance to no purpose.

If you actually don't need it (i. e., your hot methods are never overridden), then the JIT will trivially compile those "virtual" method calls as non-virtual ones. It has all the information it needs, since it knows what classes are loaded. It can invalidate its code if necessary, if you load more classes that do add overrides. So no, it does not cost performance at the call site. It does cause the compiler to do work, but nothing fancy.

> If you actually don't need it (i. e., your hot methods are never overridden), then the JIT will trivially compile those "virtual" method calls as non-virtual ones.

But isn't that the thrust of this article? Of course the JIT can optimise a monomorphic call-site. The question is, in reality, what percentage of the time will it be optimised for your users?

You can even aot compile a class and have the cached “JIT compiled” code loaded on startup.

https://docs.oracle.com/en/java/javase/13/docs/specs/man/jao...

What the article doesn't acknowledge is that there are many ways to force compilation of your code. You can change the threshold for the number of executions before the JIT is called. You can run scripts as part of your deployment that exercise the paths you are interested in. So yes, it's true that "this code runs very infrequently, but I still care about its latency" is not what JVMs might be optimized towards. But it's possible to understand the issues and solve them.
To need to understand and solve issues is always worse than not to need either.

Java got commercial success despite its many design flaws, not because of them. $1B+ promotion from Sun helped some, providing a route to freedom from Microsoft sharecropping helped more.

And from being way better than either C or C++ to write distributed applications.

Having done that with C across the major UNIX flavours around 2000 and later again with C++ and CORBA a couple of years later, it is kind of obvious why most enterprises moved into it (and its nemesis, .NET).

Java is based on Objective-C, which is based on Smalltalk. In both of those all method calls are messages, which are even more abstract than virtual functions but a lot more useful.

Java simplified things for performance but ended up with a much weaker and less expressive system, and probably didn't go hard enough for static performance either. Still, it's less of a performance issue than the lack of value types.