That was more in the early days when the GC was more primitive.
The later GC was actually several different ones. A huge impact had the so-called 'Ephemeral GC', where the machine tracks changed RAM memory (it uses virtual memory in general) and where the EGC focuses on those areas with objects which were only very short-lived. That means, given a sufficient amount of memory, the machine stayed fast and responsive for a long time.
The main problem was the low amount of RAM (20MB were common after the mid 80s), because RAM was very expensive, and the large amount of virtual memory (possibly hundreds of MB on slow disks).
Thus what made the machines really slow was the GC over virtual memory with relatively slow disks. Thus the later mainly used GC was incremental&generational©ing&with areas and with support from the EGC - a full Mark&Sweep GC would keep the machine possibly busy for 15-40 minutes.
You have a lot useless bits in a 64 bits pointers and thanks to the virtual memory, you can manage to have an untagged address and its corresponding tagged address referencing the same physical memory. This give you free bits that you can use to track the liveness/evacuation of a graph of objects.
and yes ZGC doesn't use colored pointer to track if a value is an integer or a pointer because Java unlike Lisp is typed so the VM derives those information from the bytecode.
With forwarding pointer, you can copy data and update a reference in a way that is transparent to concurrently running code, as when it access the data again the CPU (or software implementation) will notice the data had been moved and follow the forwarding pointer to new location.
The rest of the support for GC was centered around structure of virtual memory, without anything very specific to GC in hw - we're talking about things like "memory is divided into X areas, inside each area the lower side is ephemeral... " etc. There was also some register use to keep track of GC status. I think some versions had MMU or page fault handler update the GC register data.
I think for platforms like Android this makes a lot of sense. Should help quite a bit with battery consumption and responsiveness. Also makes sense for server loads in Java or Go.