a. How does Java's speed today compare to C++?
Difficult to measure. It's worth noting that a major part of the speed of an implementation, it's memory allocator, are very different algorithms in Java and C++. The non-deterministic nature of the collector makes it extremely difficult to obtain meaningful performance data in comparison to the deterministic memory management of C++, because you can never be certain what state the collector is in. This means that it's very hard to write a benchmark that might meaningfully compare them. Some memory allocation patterns run much faster with a GC, some run much faster with a native allocator.
What I would say, however, is that the Java GC has to run fast in every situation. A native allocator, however, can be swapped out for one that's more appropriate. I recently fielded a question on SO about why a C# Dictionary
could execute in (0.45 ms on my machine) compared to an equivalent std::unordered_map
which executed on (10ms on my machine). However, by simply swapping out the allocator and hasher for more appropriate ones, I cut that execution time to 0.34ms on my machine- a thirtieth of the original run-time. You could never, ever hope to perform that kind of custom optimization with Java. An excellent example of where this can make a real difference is threading. Native thread libraries like TBB provide thread-caching allocators which are massively faster than traditional allocators when dealing with many allocations on many threads.
Now, many people will talk about JIT improvements and how the JIT has more information. Sure, that's true. But it's still not even remotely close to what a C++ compiler can pull- because the compiler has, comparatively, infinite time and space in which to run, from the perspective of the run-time of the final program. Every cycle and every byte that the JIT spends thinking about how best to optimize your program is a cycle that your program isn't spending executing and can't use for it's own memory needs.
In addition, there will always be times where compiler and JIT optimizations cannot prove certain optimizations- especially in the case of things like escape analysis. In C++, then as the value is on the stack anyway, the compiler doesn't need to perform it. In addition, there are simple things, like contiguous memory. If you allocate an array in C++, then you allocate a single, contiguous array. If you allocate an array in Java, then it's not contiguous at all, because the array is only filled with pointers which could point anywhere. This is not only a memory and time overhead for the double indirections, but cache overheads as well. This kind of thing is where the language semantics of Java simply enforce that it must be slower than equivalent C++ code.
Ultimately, my personal experience is that Java could be about half the speed of C++, on average. However, there's realistically no way to back up any performance statements without an extremely comprehensive benchmark suite, because of the fundamentally different algorithms involved.
b. Would it be possible to create a modern AAA title using Java?
I assume that you mean "game", here, and not a chance. Firstly, you'd have to write everything from scratch yourself as nearly all the existing libraries and infrastructure target C++. Whilst not making it impossible per se, it could certainly contribute solidly towards unfeasible. Secondly, even the C++ engines can hardly fit in the tiny memory constraints of existing consoles- if JVMs even exist for those consoles- and PC gamers expect a little more for their memory. Creating performant AAA games is hard enough in C++, I don't see how it could be achieved in Java. Nobody has ever written an AAA game with significant time spent in a non-compiled language. More than that, it would simply be extremely error-prone. Deterministic destruction is essential when dealing with, for example, GPU resources- and in Java, you'd basically have to malloc() and free() them.
c. In what areas specifically is Java slower than C++, if at all?
(i.e. Number-crunching, graphics, or just all around)
I'd definitely go for all-around. The enforced-reference nature of all Java objects mean that Java has far more indirection and references in it than C++ does- an example I gave earlier with arrays, but also applies to all member objects, for example. Where a C++ compiler can look up a member variable in constant time, a Java run-time has to follow another pointer. The more accesses you do, the slower this is gonna get, and there's nothing the JIT can do about it.
Where C++ can free and re-use a piece of memory almost instantly, in Java you have to wait for the collection, and I hope that piece didn't go out of cache, and inherently requiring more memory means lower cache and paging performance. Then look at the semantics for things like boxing and unboxing. In Java, if you want to reference an int, you have to dynamically allocate it. That's an inherent waste compared to the C++ semantics.
Then you have the generics problem. In Java, you can only operate on generic objects through run-time inheritance. In C++, templates have literally zero overhead- something Java can't match. This means that all generic code in Java is inherently slower than a generic equivalent in C++.
And then you come to Undefined Behaviour. Everyone hates it when their program exhibits UB, and everyone wishes that it didn't exist. However, UB fundamentally enables optimizations that can never exist in Java. Take a look at this post describing optimizations based on UB. Not defining behaviour means that implementations can do more optimizations and reduce the code required to check for conditions that would be undefined in C++ but defined in Java.
Fundamentally, the semantics of Java dictate that it is a slower language than C++.
Is Java now considered a compiled language or interpreted language?
It doesn't really fit into either of those groups. I'd say that managed is really a separate category on it's own, although I'd say it's definitely more like an interpreted language than a compiled language. More importantly, there pretty much only are two major managed systems, the JVM and the CLR, and when you say "managed" it's sufficiently explicit.
What are some major shortcomings of Java that have been addressed
since the early days?
Automatic boxing and unboxing is the only thing I know of. The generics solve some issues, but far from many.
What are some major shortcomings of Java that have yet to be
addressed?
Their generics are very, very weak. C#'s generics are considerably stronger- although of course, neither is quite templates. Deterministic destruction is another major lack. Any form of lambda/closure is also a major problem- you can forget a functional API in Java. And, of course, there's always the issue of performance, for those areas that need them.