Java is a compiled language.
in my mind: interpreted = why would you ever use a language like this? VM = fast, safe, but going to use a lot of memory machine code = fastest, low level HW access, usually unsafe
This distinction doesn't make much sense in 2017: most languages are blends of all these things with technologies like VM's, JIT, Ahead-of-time compilation, etc...
Then there's interpreted code which has worked the same way for forever
A really important aspect of using an intermediate language like Java, .NET but also LLVM does is that it reduces the amount of required code. If you have M languages each targeting N different platforms, then you need M * N traditional compilers. If you first compile to a common intermediate language and then from there to the targeted platform, then you only need M + N compilers.
Do you have examples (incl. measurements) for optimizations actually performed by a Java JIT compiler, which an ahead-of-time compiler can't perform due to a lack of runtime info? It is my understanding that those analyses and transformations which eke out the last few percentage points are so expensive that they're infeasible to do at runtime.
As an example of what they can do, take de-virtualisation. Virtual method calls are expensive. Java method calls are virtual by default. The JVM profiles method calls and analyses the class hierarchy to discover which ones can be de-virtualised. That's a big win. C# requires programmers to manually specify which methods are virtual because .NET doesn't use profile-guided JIT compilation. In some cases (admittedly I've only seen artificial examples) this optimisation is so powerful you can write Java programs that completely trash C++ programs, e.g. a program that uses a command line switch to pick a subclass of a virtual base class and then runs method calls on that object in a tight loop. Java will devirtualise the call based on the observation that only one target is ever used, then inline it, then do loop opts on the inlined version.
With JIT you can set the size when you create the object, and if JVM knows that value can't change it will pull out all the branches for different sizes and run only the one for selected size.
There's no way to know which sizes will be selected at compile time if the lists can be dynamically created, so a static compiler can never pull out all the checks the list size.
This is a simple example but the JIT is very smart and makes a big speed difference in practice. It's the main reason Java is faster than C in some benchmarks
[1] http://www.oracle.com/technetwork/java/whitepaper-135217.htm...
[2] https://www.microsoft.com/en-us/research/publication/spur-a-...
I wanted to cite "Improving Java Performance Using Dynamic Method Migration", Lattanzi 2004, but the site hosting the paper isn't loading at the moment.
Forcing all objects to be heap allocated which makes things like Optional introduce even more memory indirection because thrashing your cache is totally fine
No primitives in generics which therefore means no primitives in containers
Which then leads to the fun of autoboxing so that way your bools can be true, false, or null!
This whole setup helped me develop surprisingly large number of useful tools and apps which proved successful enough that snippets of my code and pattern are copied in proper 'enterprise class' projects at our company.
I personally really like Java and I've used a ton of languages over the years. Not a perfect design but it's hard to think of any big downsides. The syntax is annoying at times but once you learn to use the IDE to poop out boilerplate it's not an issue