Just to you are aware, you are presenting a false dichotomy. There are plenty of languages that are both garbage collected and give you quite a bit of control about memory layout (D, Go, Rust, etc).
For example, in Go if I wanted have a slice of Foobar ([]Foobar) but wanted to have them allocated close to one another I could write:
const neededFoobars = 100
backingSlice := make([]Foobar, neededFoobars)
seqfoobarPtrs := make([]*Foobar, neededFoobars)
for i := 0; i < neededFoobars; i++ {
seqfoobarPtrs[i] = &backingSlice[i]
}
It doesn't. There is a type called Gc that may be garbage collected in future, but at the moment it is just a bad reference counted pointer (the Rc type is better if you want reference counting). And it's not even guaranteed that a GC will ever be implemented, Rust provides other abstractions so GC would be rarely used even if it was implemented.
For example, in Go if I wanted have a slice of Foobar ([]Foobar) but wanted to have them allocated close to one another I could write: