ยง17.3โ€“17.5Basic Strategies โ€ฆ Summary

Part I OSTEP pp. 177โ€“181 ยท ~6 min read

  • best fit
  • worst fit
  • first fit
  • next fit
  • segregated list
  • slab allocator
  • buddy allocation

No best answer exists โ€” the request stream is the programmerโ€™s whim, and any policy can be ambushed by the wrong workload. So the book does what engineers do: lay out the basic strategies, price them, and let the workload decide.

17.3 Basic Strategies

Best fit : search the whole list for candidates (chunks โ‰ฅ the request) and return the smallest โ€” closest to what was asked, least waste per grant. Cost: an exhaustive search, and a trail of tiny slivers. Worst fit inverts it: take the largest chunk, hoping leftovers stay usefully big. Also a full search โ€” and most studies show it simply performs badly. First fit grabs the first chunk big enough: no exhaustive search, but the front of the list silts up with splinters (address-based ordering helps, and eases coalescing too). Next fit adds a roving pointer that resumes where the last search ended โ€” first-fit speed, splinters spread evenly.

The book's ยง17.3 example, live: free list 10 โ†’ 30 โ†’ 20, request 15. Try each policy โ€” then keep allocating and watch the list decay.
headโ†’10โ†’30โ†’20โ†’NULL

amber outline = visited last search ยท green = chosen ยท sizes in bytes (headers ignored, as in the book's examples)

The book's outcomes: best fit โ†’ 10 โ†’ 30 โ†’ 5 (searched all 3, chose 20, left a classic tiny sliver). Worst fit โ†’ 10 โ†’ 15 โ†’ 20 (chose 30). First fit โ†’ the same list as worst here, but it stopped searching after 2 nodes โ€” that cheapness is its whole argument. Reset and try a run of small requests under first fit to watch the front of the list silt up; then try next fit and watch the rover spread the damage.

17.4 Other Approaches

Segregated lists. If an application hammers one (or a few) sizes, give that size its own dedicated list : no search, no splitting, near-zero fragmentation for that size โ€” and a new headache (how much memory does the special pool get?). The definitive answer is Jeff Bonwickโ€™s slab allocator (built for the Solaris kernel): at boot, create object caches for hot kernel objects (locks, file-system inodesโ€ฆ); each cache is a segregated list of one size, and it trades memory with a general allocator dynamically โ€” requesting page-multiple slabs when low, returning them when the objectsโ€™ reference counts all hit zero. One more trick: freed objects are kept in their initialized state, dodging the (measurably costly) init/destroy cycle on every reuse.

Aside: Great Engineers Are Really Great

Engineers like Bonwick (the slab allocator, then ZFS) are the heart of Silicon Valley. Behind almost any great product is a person โ€” or a small group โ€” way above average in talent and dedication. As the saying goes: someone exceptional isnโ€™t a little better than someone pretty good; theyโ€™re 100ร— better. Work hard and you might become such a person. Failing that, work with one; youโ€™ll learn more in a day than most learn in a month. Failing that, feel sad.

Buddy allocation. Since coalescing is what keeps a heap healthy, the binary buddy allocator designs everything around making it trivial:

Figure 17.8: a buddy-managed 64KB heap serving a 7KB request โ€” then healing itself on free
64 KB

1A 7KB request arrives

Free memory is conceptually ONE space of size 2^N โ€” here 64KB. The search for free space will recursively divide it by two until it finds the smallest block that still fits 7KB.

step 1 / 6

Other ideas. Plain lists donโ€™t scale โ€” advanced allocators reach for balanced binary trees, splay trees, or partially-ordered trees to cut search costs. And the multiprocessor era bred allocators built for concurrency: Hoard, and Evansโ€™s jemalloc (still inside FreeBSD, Firefox, and Facebookโ€™s stack). For the real world in one dive, read how glibcโ€™s malloc works.

17.5 Summary

Rudimentary allocators live everywhere: linked into every C program, and inside the OS managing its own structures. The recipe is now yours: splitting and coalescing over an embedded free list, headers to remember sizes, a fit policy chosen with the workload in mind, and โ€” when the workload cooperates โ€” segregated pools or buddy trees to make the common case fast. Building one allocator that is fast, space- efficient, and scalable for arbitrary workloads remains an open challenge. The next chapter takes the other exit: make every unit the same size, and watch the whole problem dissolve.

Homework: malloc.py

Drive a simulated allocator: predict alloc/free results and free-list states under BEST / WORST / FIRST (-p) with different list orderings (-l ADDRSORT / SIZESORT+ / SIZESORT-), then crank -n to 1000 and compare runs with and without coalescing (-C) โ€” watch what happens to large requests over time, and craft request streams (-A) that shatter the free space on purpose. Get it at ostep-homework.

Check yourself

1.Free list: 10 โ†’ 30 โ†’ 20. Request: 15. What does each strategy leave behind?

2.First fit is fast but has a known disease, and a known palliative. Name both.

3.The slab allocator keeps segregated per-size caches for hot kernel objects. What TWO tricks make it more than a plain segregated list?

4.Why is coalescing nearly free in a binary buddy allocator โ€” and what's the tax it pays for that?

5.The chapter refuses to crown a best strategy. What's the fundamental reason?

5 questions