I have written a multi-threaded socket server, relying on the Executors framework. The way I structured the program where threads are concerned is to have services that run and when they finish, they cause a callback to happen (which often causes some other service to run). Because the observer does go through it's wired-together flow, I am sure all my Runnables are completing. During the course of handling requests, I set all members that are not used by other objects to null and then I have a clean up process to set everything to null when the client is done communicating.
This socket is for pushing pixels around so the memory use is fairly intensive. When I review the memory usage of the socket, this is what I find...
- My computer is consuming 5 GB of RAM when the socket starts (I have 32 GB on this one).
- Once the first client connects and messages begin going back and forth, RAM in use will quickly grow to 9 GB (we started with 5 so 4 GB for the socket).
- Once at 9 GB, GC seems to start running as subsequent calls will cause the memory usage to grow to about 9.5 GB but I never see it get above that.
- If I close the server, the 4 GB of allocated memory is suddenly free.
I am using a lot of ByteArrayInput and Output streams in this server and I am careful to de-allocate their buffers and any byte that happen to be locally scoped. I've gone through my services and I think I've got all my null references in place. And, given that the memory peaks and the system keeps working without consuming more memory, I'm guessing that my attempts to free memory are working. Why though would the memory not go back down to 5 after the first client is done? Does the garbage collector run only when memory is needed or does it run at some generally fixed rate and reclaim memory that is no longer in use? Does it not bother to run and reclaim memory if nothing is going on? I even do a call to System.gc() at the end of the client messaging processes but that has no affect on memory being reclaimed.
Seeing this memory never drop after it gets allocated gives me pause.
Alvin Parker wrote:Does it not bother to run and reclaim memory if nothing is going on?
Yes, this is a true statement about some garbage collectors. Or at least, they don't return unused memory to the operating system, which I think is what you are observing.
And from the point of view of the garbage-collector writer, this is a perfectly rational strategy. After all you've said you are willing to allocate X megabytes of memory to run the application, so if you're only using Y megabytes, where Y <= X, why should the garbage collector bother to return any of those Y megabytes to the operating system?
However I have worked on systems where the garbage collector does in fact return unused memory to the operating system. But apparently yours doesn't.
You should use VisualVM (part of the JDK, as bin\jvisualvm.exe on Windows) to check your memory consumption. That goes inside the memory claimed from the OS and shows the separate memory segments. You should install the Visual GC plugin explicitly though, as that's where the interesting stuff is shown.
Thanks for the information! It makes sense that the system would not bother to spend resources running if memory is allocated that does not need to be reclaimed. It just struck me as something to look out for, especially when I closed the JVM and see all the memory get dumped. I am going to put on the memory profiler. Thanks for an easy suggestion on that. I just put together a new system and need to get all these tools back on. Having a simple to set up profiler is going to be useful.