Note that disk images can only be compacted if they are not in a snapshot chain and do not use disk encryption!

libVirt

virt-sparsify --compress /path/to/image.qcow2 /path/to/new-image.qcow2

VirtualBox

Windows

Disable the paging file (Settings → System → About → Advanced system settings → Advanced → Performance → Settings… → Advanced → Virtual memory → Change…), clean all temporary files (C: → Properties → Disk Cleanup), defrag (defrag C: /FreespaceConsolidate /Verbose), and then zero out free space with SDelete (sdelete -z C:).

Then, compact the disk image on the host:

vboxmanage modifymedium /path/to/image.vdi --compact

Remember to turn the paging file back on after compacting the drive!

Linux

Zero out free disk space on the guest:

sudo dd if=/dev/zero of=/temp.zeros bs=4096k
sudo rm -f /temp.zeros

Compact the disk image on the host:

vboxmanage modifymedium /path/to/image.vdi --compact