Although most of my experience is with non-GC programming, I don't really believe either GC or non-GC is universally superior.
What I will say, however, is that trading memory overheads for speed, or for other resources, is quite common - even for non-GC languages.
A naive resizeable array grows by fixed increments. When inserting a large number of items, this results in O(n^2) performance because of the O(n) reallocate-and-copy operations on the array. A faster alternative is "array doubling" - more accurately, growing by a fixed factor. This gives amortized O(n) performance for a long sequence of inserts. The cost is that, instead of having a fixed maximum memory overhead, the maximum overhead is proportional to the number of items in the array.
EDIT - silly mistake above - array gives amortized O(1) for each insert, but strictly O(n) for a series of n inserts, due to how "amortized" is defined.
Array doubling is not only applied to simple resizable vector/array types, but also to types that have resizeable arrays underlying them - the most obvious being hash tables. Without this proportional memory overhead, the amortised O(1) insert/delete times for resizeable hashtables cannot be achieved. This applies irrespective of whether garbage collection is used.
Similarly, with B trees and B+ trees (commonly used for database indexes), each node is guaranteed to be at least half full - with the exception of the root, which may even be completely empty. These data structures are more likely used on disk than in main memory, but either way they have an expected memory/storage overhead that grows in proportion with the number of items.
With garbage collection algorithms, there are also trade-offs between memory and performance. This can be seen as an extra layer on top of the data structures in some cases, adding an extra layer of waste, but this isn't a general rule - I don't know what weight to put on each two cases.
In unmanaged languages, you need some other mechanism to keep track of memory that needs to be freed anyway. Sometimes, that comes pretty much for free in the data structure itself - e.g. in a binary tree, you need the child links irrespective of whether you use them for a delete-all-items operation or not. But other times that too can involve a tradeoff between extra memory overheads and performance. For example, "memory pools" are often used to trade memory overheads (the whole pool remains allocated, even though relatively few items within the pool are in use at any particular time) for a faster and more convenient free of all items at once.
My basic point is, I guess, a variation on "avoid premature optimisation". Don't worry too much about memory overheads unless you know they will cause a problem. And be aware that when you reduce memory overheads, you are likely to pay with increases to other overheads - so do before and after measurements of more than just memory usage.
It may seem like it's too late to think of changing language when you find a problem with garbage collection memory overheads, but there are things you can do without re-writing your whole system. For example, when coding in C# for .NET, one option is to reimplement a critical data structure using some unmanaged C++.