As far as I know the big argument for C#, Java and other high level languages having to be memory managed by a runtime environment is that the programmer does not take care of garbage collection or destruction of objects (if she does not want to). But is that really something a compiler could not do? It should be fairly algorithmic to know at compile time when an object is no longer needed, so the cleanup code could be generated and inserted in the right places in the compiled file. So you could create software with these languages that are compiled to machine code and run natively. (Ok, let's ignore that these are now cross platform, let's pretend we don't need that.)
It should be fairly algorithmic to know at compile time when an object is no longer needed.
It is not. As Jörg notes in his answer, it is a provably undecidable problem in languages like C# and Java that allow for things like unsafe code, introspection, code generation, virtual dispatch, and user casting.
For example, consider code where you pass a reference as an argument to an interface's function. The implementation of that function might not even be in the code you're compiling. How could you possibly know what the function does with the reference?
Some cleanup code is easy to know where to insert and some not. The memory that is easy to clean up is usually allocated on the stack. Heap memory is trickier to know statically when it is safe to clean up. Think of things like a shared list of items that is added and deleted from various functions.
Your idea is the premise behind the Rust programming language, and they could only achieve it by requiring some awkward restrictions and annotations about reference sharing. Even then, there are certain situations where they have to fall back to reference counting.
Put another way, garbage collection may be somewhat slower, but it is much easier to implement in a programming language, and requires fewer restrictions and demands of the programmer.
Many compiled languages including C++ do offer nice things like "reference-counted objects" and interpreter-style memory management. But, they necessarily require your discipline in order to use them properly. As long as you do that, they work great. (But, if you screw it up, they can't help you.)
Interpreters, on the other hand, control their entire implementation themselves. They never "do the wrong thing," and they don't allow the language-user to do so either.