Claiming a whole block for a small request wastes memory. Today Malloc splits an oversized free block, carving off exactly what is needed and leaving the remainder as a smaller free block - unless that remainder would be too small to be a block.
Split a free block on allocation, leaving the leftover as a free block only when it meets the minimum block size.
Taking a whole 64-byte block to satisfy a 16-byte request wastes 32 bytes. Splitting fixes that: carve off exactly the block size needed at the front, mark it allocated, and write a fresh free block in the leftover space. A 16-byte request needs a 32-byte block, so a 64-byte free block splits into a 32-byte allocated block and a 32-byte free remainder.
The one subtlety is the minimum block size. A block must be at least 24 bytes (16 overhead plus 8 payload) or it cannot even hold its own tags and a free-list pointer. So split only when the remainder would be a legal block: if the leftover is exactly 24 it splits, but if it would be 16 you must not - instead give the caller the whole block (a little internal waste beats an unusable sliver). Pin both sides of that boundary now; the “exactly the minimum” case is the one that is easy to get wrong.
// found a free block at off with total Size; need = blockSize(n)if size-need >= minBlock {a.putBlock(off, need, true) // allocated parta.putBlock(off+need, size-need, false) // free remainder} else {a.putBlock(off, size, true) // take the whole block}return off + 8