Each time you malloc() some memory, a bit more than what you requested is actually used -- the malloc system uses some bytes before or after (Or both) what it returns for its own bookkeeping. With large allocations, this doesn't matter much, but with lots of small allocations, it can cause problems. Some malloc implementations are better than others. A slab allocator can be used to reduce much of this overhead no matter how the underlying malloc implementation acts.
Before 1.8.3p4, attribute and lock structures were stored in their own, simple, slabs. In 1.8.3p4, a more general purpose and powerful allocator was added, and many areas of the source converted to use it.
The slab allocator is intended for small, fixed-size objects — structures, basically. strings are not suitable unless they are all the same length. Object sizes should be well under the page size of the host computer's virtual memory system — usually 4K, sometimes 8K.
The allocator uses malloc() to allocate a page's worth of memory at a time, and crams as many objects into that as it can. It keeps track of how many objects in each page are being used and how many are free, so it can delete any unused pages.
When allocating objects from a slab, you can hint as to what page should be looked at first for a free object. This is used to improve cache behavior (For example, when iterating through all attributes on a database object, it's faster if most of the structs are on the same page.).