If the range of values is limited to 32bit integers, a simple way would be to keep a 4GiB array of 8bit saturating counters.
- After one pass, if only one counter hit 255, then that's the value repeated the most.
- otherwise, record the values for all the counters that saturated to 255.
- Make another pass through the file, only updating 64bit counters for the values that you recorded. (ignore others).
You could convert to using longer counters on the fly to keep it a one-pass algo. 255 is a sentinel value for a counter, meaning that you should instead refer to a hashmap of values -> 64bit counters.
You could use 4-bit saturating counters if 4GiB is too much, but then more of your counters will saturate, and they'll be slower to update (although memory will still be the bottleneck, regardless of the extra instructions to shift/mask/recombine-with-old-value).
There's no point in using a multi-level approach (1 bit saturating counters, then 8-bit saturating counters, ...), because all levels after the first have to be sparse (or there's no point). The per-entry overhead of a sparse map, like a hash or tree, is going to be dominate the size of the actual counter, so use as much memory as you can for the dense first-level, then fall back to a hashmap of 64bit counters for the 2nd level.
If a dense array of counters isn't viable at all (e.g. long numbers)
Sort in batches while counting duplicates, then merge those batches. See for example my answer to Memory-constrained external sorting of strings, with duplicates combined&counted, on a critical server (billions of filenames) for suggestions about how to maximize efficiency when batching things up. That was targeted at strings, not integers, but approaches like Tries for counting duplicates on the fly with good space efficiency will work even better for strings of digits than for arbitrary strings. A Radix Trie (nodes can represent strings, not just characters) might be more trouble than it's worth for such a small alphabet.
In any case, if sorting, count duplicates until you've used as much memory as you have available before writing a batch. Every duplicate you find and count in the first pass is one that won't have to get merged later.