Low pause collectors exist explicitly for this purpose. ZGC and Shenandoah both trade throughput (more concurrent collections) for extremely low pause times: https://malloc.se/blog/zgc-jdk16
Does that mean they also trade _memory_ for low pause times? Because, in our application, the memory required for avoiding significant GC pauses is at least 4x the amount of memory we actually need at any given time.
Have you measured that with ZGC? Because due to using virtual pointers specifically, ”third-party” tools may report higher virtual memory usage than what it actually uses.