When constructing an InputStream on a RandomAccessFile in order to have Kryo deserialize objects from it, it seems it makes a HUGE difference for performance whether one constructs the mediating InputStream through the file's Channel (gives good performance) or through its FileDescriptor (gives terrible performance):
RandomAccessFile ra = new RandomAccessFile(dataFile, "r");
Input input1 = new InputWithRandomAccessFile(Channels.newInputStream(ra.getChannel()), FILE_BUF_SIZE, ra);
Input input2 = new InputWithRandomAccessFile(new FileInputStream(ra.getFD()), FILE_BUF_SIZE, ra);
InputWithRandomAccessFile is my own class extending Kryo's Input class, with the only additional behavior that it seeks the correct position in the R/A file when #setPosition is called.
Reading 3,000 fixed-size objects from input1 takes around 600 ms, from input2 it takes around 16 seconds.