Yeah, I was pretty incredulous when I first discovered over-commit in Linux - I asked for memory and you gave it to me without an error, and now that I am deep in the guts of processing and can't easily recover, now you decide to tell me you don't really have it!
But once you know about over-commit there are workarounds in languages where you control memory allocation, like touching every page right after you allocate it, but before using it. And in a garbage collected language you don't have any control or insight into when OOM exceptions will occur in either approach. So the ability for the OS to not trust lazy and greedy software that asks for memory but doesn't use it seems like a reasonable trade-off.
I think one of the main reason overcommit is a thing on Linux (certainly one of the things one runs into if one turns it off) is dealing with the weirdness of fork()/exec(). For a moment there in between the two calls you _technically_ have double the RSS allocated - so spawning processes with fork()/exec() from a process with very large RSS is dicey if you don't want to technically overcommit the memory. Since 99.9% of the time very little of that memory is touched/COW'd before the exec() letting it overcommit rather than dying when a 4GB process tries to spawn a new child just because you don't have another spare 4GB of ram sitting around "just in case" is seen as a reasonable tradeoff.
(Modulo vfork() and spawn() of course which are different and arguably better solutions to this issue.)
But once you know about over-commit there are workarounds in languages where you control memory allocation, like touching every page right after you allocate it, but before using it. And in a garbage collected language you don't have any control or insight into when OOM exceptions will occur in either approach. So the ability for the OS to not trust lazy and greedy software that asks for memory but doesn't use it seems like a reasonable trade-off.