The simplest solution would be to not allow freeing of allocated memory. Assume the the operating system has a heap of memory it knows is available for programs to allocate from. The operating system keeps track of an address at the start of this free area. if there is enough memory available for the first alloc that alloc gets the free pointer address and the free pointer moves forward by the amount of the alloc. And this continues to move forward until there is no more memory or nobody allocates any more. so if the starting address was 0x1000 and someone wanted 0x20 bytes they would get 0x1000 returned as the pointer for the alloc and the free pointer moves to 0x1020. The next alloc of say 0x100 returns 0x1020 and the free pointer moves to 0x1120, etc.
You could also work down from a high address, top down instead of bottom up. Top of memory might be 0x10000, first alloc of 0x20 results in 0x10000-0x20 = 0xFFE0 so 0xFFE0 is sent to the application and is also saved as the top of free memory. The 0x100 alloc gets 0xFEE0, etc.
The problem with this simplistic idea, never freeing memory, is that you run out of memory quickly. By that time much if it may have been orphaned by programs that have completed. so it might work in some embedded systems, but in general something more has to be done.
So not unlike a file system a table has to be maintained by the memory allocation system (operating system) that contains at least the start address and amount of data allocated for each alloc that has occured. Then some sort of search algorithm goes through that list trying to determine open chunks of memory with which to complete an alloc. When a free happens then it might be a simple manner of finding the entry with a matching address and deleting it from the table. Making a table structure that is fast to parse, sort, whatever, but also allows for small and large allocs without too much wasted memory.
Mmus can make this much easier, partly because they are driven by tables already, the hardware navigates the mmu table to find the physical address among other things. The big feature here is that you can take chunks of memory that are not next to each other in physical address space but can be placed linearly in virtual address space. So your application may want 12345 bytes, and your mmu system may have its smallest memory division being 4kbytes, so you simply need to find four free chunks of memory that dont have to be remotely near each other, you do have to find four mmu table entries that are next to each other, and you can point those four mmu entries at the four separate physical entries creating a 16kbyte space which is allocated to that application. You still need some other table outside the mmu system that tells you these four mmu entries are part of the same allocated space. This is not unlike a file system where a 12345 byte file might be stored in 4 4096 byte sectors that are not necessarily next to each other on the disk. The directory structure and file system tables use a link list or some other method of keeping track of what sectors are consumed by that one file.
As far as the management of that memory, what physical address to choose, etc. Well over the decades of operating systems there are have been countless solutions, countless hours of experiments and research. Think about the host at a restaurant. You come in with three people and want a table. How many different ways have you seen the table chosen? Sometimes they have a chart and a wax pencil, sometimes they just wander into the seating area and find a table. You may or may not realise but sometimes they are not just trying to find any four top hopefully and in desperation six top, but they are also trying to balance the number of customers per waiter/waitress. They are probably also trying to leave chunks of tables available just in case a 8 or 10 person party comes in they can push tables together. For as many different restaurants and hosts there are there may be as many different solutions for allocating a table for your party. Likewise from the early timeshare computers to the present there have been as many different solutions for memory allocation.
There is nothing to stop you from trying this out yourself. Write a program that takes a chunk of memory and make your own malloc and free routines that try to divide that chunk of memory up to serve the malloc and frees, and maybe take a randomiser and use it to call your malloc and free routines.