The way I see it the issue is twofold.
Separate objects benefit from instancing optimizations. If objects share the same object data (geometry) Blender only stores such geometry once, and objects share it internally.
This yields both smaller file size and corresponding savings reading and loading the file. Once the scene is loaded memory is also saved by only recycling the shape of the object both into RAM and VRAM.
Joining all into one objects removes the benefit of instancing, and worsens it by creating a massive single mesh full of unique data.
Other than that joining, separating or preforming any other operation on objects creates undo steps which are stored in memory, requiring additional memory consumption until it is flushed or released.
Overall joining objects doesn't really add any benefit at all, other than possible viewport performance and working responsiveness. Blender tends to get slow when the object database is very large, and the viewport tends to slow down if large amounts of objects are visible.
Under certain situations Blender may behave faster with a single large mesh as opposed to several thousands of instances containing the same geometry, generally at the expense of memory consumption, editability and file size.