How does memory management work in Java, and how do you diagnose a memory leak in a running application?