Performance is nice, but correctness is more important. Complex programs written without any GC tend to have use-after-free and double-free vulnerabilities. Programs written using automatic reference counting only (without a way to catch cycles) tend to have memory leaks. If GC is killing performance, then that is a failure of the specific GC or programming language design. Don't blame the applications programmers for wanting GC semantics; that's a completely reasonable thing to want and something that language designers are capable of providing.
In the specific case of Java, I think the real problem is not having value objects (like C#'s struct) or generics with non-reference types. Serious GC problems happen not because GC is too hard, but because in Java an ArrayList<int> of a million items makes a million tiny objects. This problem is entirely Java-specific.
Back when Java was still in its infancy, we had Oberon(-2), Component Pascal and Modula-3 as system programming languages that allowed for C like memory allocation, coupled with a GC.
But Java was the one that made GCs finally go mainstream and now many equate GC with Java ones[0], thinking all GC are made alike.
[0] Forgetting in the process that Hotspot is just one JVM among many.
> that's a completely reasonable thing to want and
> something that language designers are capable of
> providing.
This is exactly what the designers of Rust have worked to provide: freedom from GC while maintaining guarantees against user-after-free and double-free, while allowing you to reference-count bits of data when you ask for it (RC leaks are rare in Rust--it's not trivial to create cycles--though still possible).
Specifically about ArrayList<Integer> -- there are specifically a bunch of projects (Goldman Sachs Collections, trove, FastUtil, Koloboke) that solve the array of primitives problem.
I would say that's a Java-standard-libary-specific problem.
And then you have to do a copy just to pass it around to any other external libraries. And you lose the advantages of generics.
You end up with 9 copies of everything, that are 99% the same except for a couple find-replaces. If not more. (For instance, if you have a method that takes two generic arrays, you need 81 copies! Even if they are the same type you still need 36 (!) copies.)
How do you end up with 9 copies of everything? I'm not sure I follow that.
If you are passing in massive ArrayList<Integer> lists to external libraries, you need to re-evaluate what you're doing. What will that external library do with this?
Look at java.util.Arrays for a concrete example of the problem:
static int binarySearch(byte[] a, byte key)
static int binarySearch(char[] a, char key)
static int binarySearch(double[] a, double key)
static int binarySearch(float[] a, float key)
static int binarySearch(int[] a, int key)
static int binarySearch(long[] a, long key)
static int binarySearch(Object[] a, Object key)
static int binarySearch(short[] a, short key)
This, sort of works. It's a lot of code duplication, but it's all in the library.
Except that a relatively common thread goes like this:
You start by having a generic array that gets passed to said binary search. It works, but it's too memory-hungry when it gets called with a primitive. So, then what do you do?
Well, you go "huh, I could specialize". And then you start specializing, and realize that every function that calls binarySearch with a generic type also needs 8 implementations (would be 9, but no sane person would binary search a boolean[] ).
Basically, its complexity that you cannot even punt off to an external library. If you have code that needs to be able to be called with generics without inefficiencies for primitive types, you'll end up with ~9x code duplication at a minimum.
In the specific case of Java, I think the real problem is not having value objects (like C#'s struct) or generics with non-reference types. Serious GC problems happen not because GC is too hard, but because in Java an ArrayList<int> of a million items makes a million tiny objects. This problem is entirely Java-specific.