Paging and swapping are separate things you need to worry about.
The only things that will ever be paged (not swapped) to disk are things that are mapped to files. E.g. mmaped files (or MappedByteBuffers), and code. Anonymous memory (the sort you get with malloc and when you explicitly ask to mmap with MAP_ANONYMOUS) will not get paged. So if your off-heap stuff is created with newly allocated DirectByteBuffer (which uses anonymous memory) it won't get paged. But if it's created with MappedByteBuffer of an actual file, expect paging. E.g. if you need a persistent journal file, you you've signed up for paging.
Anonymous memory is still susceptible to swapping. The best way to prevent that is to disable swapping (it's a useless and arcane thing, anyway). You can do that y simply not having a swap file, which will truly make sure it won't happen. Short of that, you can "hint" that you really wish the OS didn't swap memory for no reason by setting swapiness to 0. But I've seen systems swap with swapines set to 0 under various conditions. For example, you can end up swapping with swapiness to 0 even when half the memory in the box sitting empty in your zone_reclaim_info isn't set to 0.
A common reason people end up paging without wanting to is sharing. The easiest way to share memory between processes is with an mmap of a shared file, and if this file has an actual backing store, the OS will end up paging it. There are various ways to try to avoid and reduce this effect. One is to place the shared file in /tmp , which is usually not backed by a storage device. Another is to use shared memory that is not mapped to a file, but created using variants of shmat (the system V shared memory service) [this usually involves creating Direct buffers with JNI].