Well, the problem is that doing explicit memory management in a language that's built around GC is usually far less convenient than doing explicit memory management in a language that isn't (like C++). For example, in C++ you can use templates and placement new to write generic free lists that get specialized to the type of object in use at compile time, and they still allow you to use the regular old "new" syntax for calling the constructor. By contrast, you can write free lists in Java/C#/Python/Ruby/JS/etc, but you won't get that nice "new" syntax. (Not to mention that you don't get the same control over memory layout that you do in C++.)
Explicit memory management in GC'd languages is also less predictable: Java can allocate objects on the stack via escape analysis, but if the object is determined to escape then it gets silently demoted to the heap instead. In performance-critical code, this silent demotion can lead to a hard-to-find bug.
To me, the obvious solution is to build constructs into our GC'd language that nevertheless make explicit memory management predictable and convenient when the programmer wants to use it. A requirement for predictability is that whether an object is garbage collected needs to somehow be encoded into the type of that object (either via a static type or via a dynamic type). This is the philosophy we're following in Rust, and it's also the philosophy some of my colleagues are working on for JavaScript (via the Binary Data API).
What's nice about that syntax? In every language that I've used which has it (C++, Java, C#, JavaScript), it's been a royal pain in the butt. It makes it harder to swap in alternate factory behavior and requires remembering a few arbitrary extra rules, like operator precedence (no matter how many times I write `new Foo().bar()`, it still doesn't parse right in my head).
Ruby, SmallTalk, and Objective C get this right: Allocation is distinct from initialization (new vs init) and both use standard message passing / method call semantics.
We need semantic memory, but there is a limit to how far a language can take this. Ultimately you are sitting on top of void* malloc(size_t, size_t). A systemic rethink from HW on up is in the cards. Whether it will happen in 10 or 20 years, I am not sure, but it will happen.
Weak references are one such construct, and in AS3 are a real life-saver. Ironically, though, the popular problem is that things are collected too fast...when the real issue is that some things are never collected, and no one notices. I think GC languages would do well to provide an interface for monitoring not just memory usage, but performance impact of the GC process itself.
Explicit memory management in GC'd languages is also less predictable: Java can allocate objects on the stack via escape analysis, but if the object is determined to escape then it gets silently demoted to the heap instead. In performance-critical code, this silent demotion can lead to a hard-to-find bug.
To me, the obvious solution is to build constructs into our GC'd language that nevertheless make explicit memory management predictable and convenient when the programmer wants to use it. A requirement for predictability is that whether an object is garbage collected needs to somehow be encoded into the type of that object (either via a static type or via a dynamic type). This is the philosophy we're following in Rust, and it's also the philosophy some of my colleagues are working on for JavaScript (via the Binary Data API).