Return a newly-allocated block of memory of the specified size (in bytes). If size is 0, a null pointer is returned. Otherwise, invoke the allocate method of the allocator supplied at construction, increment the number of currently (and cumulatively) allocated blocks, and increase the number of currently allocated bytes by size. Update all other fields accordingly; if the allocation fails via an exception, numAllocations() is incremented, lastAllocatedNumBytes() is set to size, and lastDeallocatedAddress() is set to 0.