Another common trick along those lines is pointer tagging, where you use some of the bits in a pointer/reference to store flags and if those are set, put data in there directly.
E.g. you could on a 32-bit machine reserve the least significant bit to mark an integer, and if it is set the high 31 bits are treated as a 31-bit integer instead of a pointer. Since your objects are likely bigger than a byte, you don't actually need all 32 bits for addresses and thus don't even loose addressable memory space. On 64bit machines you can do it even more, since they are far from being able to use all bits for actual memory, and you might even fit a short string or other more complex type in there.