Hacker News new | ask | show | jobs
by pkolaczk 1637 days ago
The only overhead memory used by a pool allocator is the rounding to the page size. The difference from a compacting GC is that a pool allocator can allocate from the freed memory immediately after the memory was freed. So the overhead does not depend on the allocation rate, it is just a tiny constant factor.

As for the energy efficiency, I seriously doubt that bringing all memory into cache once in a while, including memory that is not needed frequently by the application, only in order to find live vs dead memory is all that energy efficient. The allocation itself is indeed typically slightly faster but the marking and compaction is additional cost you don't have to pay in manual memory management.

1 comments

Hence why I'd suggest using partial GCs like the Train, as that would have better locality of reference almost all the time. A generational GC could have similar effects, but nurseries seem to be much larger than caches nowadays, with few exceptions.
Partial, generational or region based GCs still need to scan the whole heap from time to time. By bringing stuff once a while into cache they also push stuff that's actively used out of cache. Those effects are typically not visible in tiny benchmarks that allocate temporary garbage in a loop, but can get pretty nasty in real apps. LRU-cache-like memory use patterns are particularly terrible for generational GCs - because the generational hypothesis does not hold (objects die old).

Also using generational algorithms does not remove the dependency of the memory overhead on the allocation rate. Those techniques improve the constant factor, but it is still an O(N) relationship, vs O(1) for a manual allocator. If the allocation rate is too high there are basically two solutions: (1) waste more memory (use very big nurseries, oversize the heap) or (2) slow down / pause the mutator.

The industry seems to prefer (1) so that probably explains why I never see Java apps using <100 MB of RAM, which is pretty standard for many C, C++ or Rust apps; and 50x-100x memory use differences between apps doing a similar thing are not that uncommon.

> By bringing stuff once a while into cache they also push stuff that's actively used out of cache.

I may very well be wrong, but I don’t think it is any worse than the occasional OS scheduling/syscall, etc. GCs happen very rarely (unless of course someone trashes the GC by allocating in hot loops)

Also, while a destructor is indeed O(n) it is a cost that has to be paid on the given thread, while GCs can amortize it to a separate thread.