Although jvm has a garbage collection mechanism, if the program is written without paying attention to some specific rules, it can still lead to memory leaks in java programs, which may end up with OutOfMemory exceptions.
1. Causes of memory leaks in Java
Objects in java are divided into 2 types in terms of usage, referenced and unreferenced. Garbage collection only recovers objects that are not referenced. Referenced objects, even if they are no longer used, are not recycled. Therefore, if the program has a large number of referenced and useless objects, there is a memory leak.
2. java heap memory (Heap) leak
The size of jvm heap memory is specified by two parameters -Xms and -Xmx.
2.1 Objects referenced by static members
Memory leaks are caused when large objects are referenced by static members.
The ArrayList is a dynamically allocated object on the heap and will normally be reclaimed by gc after use, but in this example, being referenced by the static member list, which is not reclaimed, will cause this very large ArrayList to remain in heap memory.
So you need to pay special attention to the way static members are used and avoid static members referencing large objects or objects of collection type (like ArrayList etc.).
2.2 String’s intern method
Calling String.intern() method on a large string, intern() will place the String in jvm’s memory pool (PermGen ), which is not gc. Therefore, if a large String calls the intern() method, it will generate a large amount of memory that cannot be gc, resulting in a memory leak.
If you must use the intern method with a large string, you should adjust the size of the PermGen memory with the -XX:MaxPermSize parameter.
2.3 Not closing the stream after reading it
Streams are often forgotten to be closed in development, which can lead to memory leaks. Because each stream corresponds to an open file handle at the OS level, streams that are not closed will cause the OS file handle to remain open, and jvm will consume memory to keep track of the OS open file handle. Example.
To solve this problem, in versions of java prior to java8 you could add a close operation to the finally.
The try-with-resources statement can be used in java8 to.
For network connections and database connections, etc. also pay attention to the closure of the connection. If a connection pool is used, then the closure operation is the responsibility of the connection pool and can be handled without the program.
2.4 Adding objects that do not implement the hashCode() and equals() methods to a HashSet
This is a simple but very common scenario. Normally a Set will filter duplicate objects, but without the hashCode() and equals() implementations, duplicate objects will keep getting added to the Set and never have a chance to be removed.
It is therefore a good programming practice to add implementations of the hashCode() and equals() methods to all classes. This can be easily done with Lombok’s @EqualsAndHashCode.
3. Methods for finding memory leaks
3.1 Logging gc logs
By specifying -verbose:gc in the jvm parameter, you can log the details of each gc, which can be used to analyze the memory usage.
3.2 Performing profiling
Memory analysis is performed through Visual VM or Java Mission Control that comes with jdk.
3.3 Code review
Discover the error codes that cause memory leak problems through code review and static code inspection.
Code level inspection can help find some of the memory leaks, but memory leaks in production environments are often not easy to find in advance because many of the problems only occur in large concurrency scenarios. Therefore stress testing with stress testing tools is also needed to detect potential memory leaks in advance.