Classic MacOS did, but it's definitely not something needed for multitasking without an MMU. For instance AmigaOS didn't do this, but instead effectively had a single shared heap.
Mac OS, Win16, PalmOS all have shared heaps too. This is precisely why you need defragmentation (after an application quits, the heap is a fragmented mess, full of holes) and therefore some system so that the other applications keep "movable handles" to heap blocks instead of raw pointers (which would become invalid after the heap undergoes one round of defragmentatino).
If an OS does not do this you are basically indirectly setting a limit to its uptime, as eventually this global heap's fragmentation will prevent launching any new programs.
Having local heaps does not solve this, as you still have to allocate these local heaps from somewhere. Having an MMU will allow you to do transparent defragmentation without handles as raw pointers (virtual addresses) become your handles. Having an MMU with fixed page size will allow you to outright avoid the need for defragmentation.
Mac OS didn’t. It had a system heap and a heap for the running application. Once it supported running multiple applications simultaneously, each of them had its own heap (https://www.folklore.org/Switcher.html: “One fundamental decision was whether or not to load all of the applications into a single heap, which would make optimal use of memory by minimizing fragmentation, or to allocate separate "heap zones" for each application. I decided to opt for separate heap zones to better isolate the applications, but I wasn't sure that was right.”)
That’s why MultiFinder had to know how much RAM to give to each application. https://en.wikipedia.org/wiki/MultiFinder#MultiFinder: “MultiFinder also provides a way for applications to supply their memory requirements ahead of time, so that MultiFinder can allocate a chunk of RAM to each according to need” (Wikipedia doesn’t mention it, but MultiFinder also allowed users to increase those settings)
It's in fact one of the biggest issues with AmigaOS that made it incredibly hard to add proper MMU support. The OS is heavily message-passing based, and it's not at all always clear "from the outside" who the owner of a given structure passed via a message port (which is little more than a linked list) is, and so the OS doesn't even know which task (process/thread - the distinction was pretty meaningless due to the lack of memory protection) owns a given piece of memory.
Later versions added some (optional) resource tracking to make it easier to ensure resources are freed, but if an application crashed or was buggy you'd frequently leak memory, and eventually have to reboot. It was not great, but usually less awful than it sounds with sufficiently defensive strategies.
[I have at various points when e.g. doing some work on AROS way back, argued that it is is quite likely possible to largely untangle this; partially because for a lot of cases, the ownership changes are clear and rules that fit actual uses can be determined; partially because the set of extant AmigaOS apps is small enough you could "just" add some new calls that does ownership tracking, declare the old ones legacy, and map ownership changes for the rest one by one and either patch them, or, say, add a data file for the OS to use to apply heuristics; had the remaining userbase been larger maybe it'd have been worth it]
That situation doesn't prevent an MMU and virtual memory. It prevents multiple address spaces. Multiple address spaces per process are not a requirement for virtual memory, as such. They are a requirement for getting some of the protection benefits of virtual memory. Not all the benefits. With a single address space for all applications, there can still be user/kernel protection: userland not being able to trash kernel data structures. (Of course with important system functions residing in various daemons, when those processes get trashed, it's as good as the system being trashed.)
It doesn't "prevent" an MMU and virtual memory, you're right, but it does severely limits what you can do with it hence why I wrote "proper" MMU support. There are virtual memory solutions for AmigaOS, though rarely used. There are also limited MMU tools like Enforcer, but it was almost only used by developers. AmigaOS4 has some additional MMU use, and there has been work on trying to add some more protection elsewhere as well, but it is all fairly limited.
Specifically in terms of the comment I replied to, you categorically can not automatically free memory when a task (process/thread) ends in AmigaOS without applications-specific knowledge without risking causing crashes, because some memory "handoffs" are intentional.
> With a single address space for all applications, there can still be user/kernel protection: userland not being able to trash kernel data structures.
Yes, you could if the OS was designed for it, and it was done at a point where most of the application developers were still around to fix the inevitable breakage.
The problem with doing this in AmigaOS without significant API changes or auditing/patching of old code is that there is no clear delineation of ownership for a lot of things.
This includes memory in theory "owned" by the OS, that a lot of applications have historically expected to be able to at least read, and often also write to.
You also e.g. can't just redefine the "system calls" for manipulating lists and message queues to protect everything because those are also documented as ways to manipulate user-level structures - you can define your own message ports and expect them to have a specific memory layout.
More widely, it includes every message sent to or received from the OS, where there's no general rule of who owns which piece of the message sent/received. E.g. a message can - and will often - include pointers to other structures where inclusion in the message may or may not imply an ownership change or "permission" to follow pointers and poke around in internals.
To address this would mean defining lifecycle rules for every extant message type, and figuring out which applications breaks those assumptions and figuring out how to deal with them. It's not a small problem.