fine-grained locking for huge allocations (but not the default chunk allocator)

This does succeed in removing the huge allocation code as a bottleneck, but the default chunk allocator has a global lock so it only kills the serialization in the code paths that are not obtaining or releasing chunks like a no-op reallocation. This same technique would work for the chunk allocator, but unlike this part it would come with drawbacks. It would make sense as another runtime option but I'm unsure about the default. The chosen hash function is quite bad, but the good hash function that's already in-tree is designed for strings and would be very expensive here.