This is just a quick post, excuse my brevity.
I often see people wanting to see why their server uses so much RAM. This post is my attempt to explain a few of the concepts behind "high memory usage".
Getting the technical stuff out of the way
Java does not allocate memory as needed. Instead, it allocates some of it initially and partitions it into several areas called generations. We are not going to take a deeper look into generations, and instead focus on what it does when that allocated memory is nearly full.
What happens when the memory gets nearly full?
As mentioned, Java does not allocate memory on demand. The main reason for this is mainly for performance reasons. Allocating memory is not fast. Instead, when memory usage reaches a critical level, it runs something called a garbage collector. The garbage collector examines every object that has been allocated, and checks if the object is no longer referenced anywhere. If so, it will remove that object.
The garbage collector is very intelligent, but there are cases where it falls short, and most are due to poor coding practices. For example, some plugins will store player objects into an area that is always referenced (perhaps it is a listener, for example). What if the player disconnects? The object is removed from the Minecraft server internally, but the plugin still has it, and therefore Java still thinks the object still exists, and will not collect it. This is called a memory leak, and it is usually occurs slowly. Eventually, the garbage collector is thrown into a frenzy and your server will start lagging and may possibly crash.
If the garbage collector does not find much to free up, only then does Java will allocate more memory, if the maximum memory allocation is not fully used. If it is reached, then Java will eventually crash with a dreaded OutOfMemoryError.