Implement A Contiguous Memory Allocator With Free-Block Coalescing
OpenAISoftware Engineer · Technical ScreenLast updated
Design a fixed-size contiguous memory allocator supporting allocation and deallocation with immediate adjacent free-block coalescing.
Problem Statement
Design a contiguous memory allocator for a memory pool of fixed size total_size bytes (addressed from 0 to total_size - 1). Implement the MemoryAllocator class with the following methods:
__init__(total_size: int): Initializes the allocator with a single contiguous block of free memory of sizetotal_sizestarting at address0.allocate(size: int) -> int: Finds the first available free block with a size of at leastsizeusing a first-fit strategy (i.e., the one with the smallest starting address). If found, allocatessizebytes from the beginning of that block and returns its starting address. If no single contiguous block can accommodatesize, return-1.free(address: int) -> bool: Frees the previously allocated block that begins precisely ataddress. Ifaddressis valid and currently allocated, free it, merge (coalesce) it with any physically adjacent free blocks (left, right, or both), and returnTrue. Ifaddressdoes not correspond to the start of an actively allocated block, returnFalsewithout modifying state.
Class Interface
class MemoryAllocator:
def __init__(self, total_size: int):
pass
def allocate(self, size: int) -> int:
pass
def free(self, address: int) -> bool:
pass
Examples
Example 1
allocator = MemoryAllocator(100)
allocator.allocate(20) # returns 0 (Allocated [0, 19], remaining free [20, 99])
allocator.allocate(30) # returns 20 (Allocated [20, 49], remaining free [50, 99])
allocator.allocate(40) # returns 50 (Allocated [50, 89], remaining free [90, 99])
allocator.free(20) # returns True (Block [20, 49] freed; free blocks: [20, 49], [90, 99])
allocator.allocate(25) # returns 20 (Fits in [20, 49]; allocated [20, 44], remaining [45, 49])
allocator.free(0) # returns True (Block [0, 19] freed)
allocator.free(20) # returns True (Block [20, 44] freed; coalesces with [0, 19] and [45, 49] -> [0, 49])
allocator.free(20) # returns False (Address 20 is no longer an active allocation start)
Constraints
1 <= total_size <= 10^91 <= size <= 10^9- Up to
10^4calls toallocateandfreein total. - Memory addresses are 0-indexed integers.
Edge Cases to Consider
- Requesting an allocation larger than
total_sizeor larger than any available contiguous segment. - Requesting allocation when memory is fragmented (sum of free memory >=
size, but no single chunk is large enough). - Freeing with coalescing on both sides simultaneously (left neighbor and right neighbor are both free).
- Freeing an address that points into the middle of an allocated block (should return
False).
Hints
Hint 1
Maintain the free segments in sorted order of their starting addresses to make first-fit searching and adjacent coalescing straightforward.
Hint 2
Track active allocations in a hash map mapping starting address to allocated block size so that `free(address)` can validate the request in O(1) time.
Hint 3
When freeing a block, look at the predecessor and successor free intervals; if they are contiguous (e.g., `prev_start + prev_size == cur_start`), merge them into a single interval.
Follow-up questions
- How would you modify the data structures to implement a 'best-fit' allocation strategy in O(log n) time instead of O(n)?
- How can you handle concurrent allocations and deallocations while minimizing lock contention?
- How would you extend this allocator to support alignment constraints (e.g., allocating memory at an address divisible by 64 or 4096)?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.