Most developers know that the garbage collector in Java is not a universal mechanism that allows a programmer to completely forget about the rules for using memory and when his work is performed. The following are typical cases of memory leaks in java-applications that are ubiquitous.
So, what should every java-programmer remember.
Typical memory leak situation:

The garbage collector periodically collects unused objects, but we see that the heap usage schedule creeps up steadily and confidently.
What can cause this situation?
')
String operations
The most common situation is when memory leaks occur in Java applications.
For understanding. Because of what problems occur when working with strings, recall that in Java, when you perform operations such as calling the substring () method on a string, an instance of String is returned only by the changed values ​​of the length and offset variables, the length and offset of the char sequence. At the same time, if we get a string 5000 characters long and just want to get its prefix using the substring () method, then 5000 characters will continue to be stored in memory.
For systems that receive and process multiple messages, this can be a serious problem.
In order to avoid this problem, you can use two options:
String prefix = new String(longString.substring(0,5)); //
String prefix = longString.substring(0,5).intern(); //
An important note to the second variant with
internals from
Zorkus : interned strings are not stored in the heapspace, but in the permgen space. Garbage collection in it occurs by separate rules, not as in heap-e / young / tenured memory pools.
Similarly, we must remember that a similar problem occurs when using the split () method.
ObjectInputStream and ObjectOutputStream
The ObjectInputStream and ObjectOutputStream classes store references to all the objects they work with to pass them instead of copies. This causes a memory leak during continuous use (for example, with network interaction).
To solve this problem, you must periodically call the reset () method.
Threads and their stack
Each instance of the Thread class in Java allocates memory for its stack (by default, it is 512 KB; it is changed using the -Xss parameter). Non-optimized applications that use multiple threads can lead to unreasonably high memory consumption.
Non-static inner classes
Every non-static inner class that you use stores a reference to an outer class. This leads to the storage of a large object graph, which negatively affects memory usage. In situations where you clearly do not need to use a reference to an external class, implement the inner classes as static.
Observer pattern and related threats
Often the situation with a memory leak occurs when using the Observer pattern. As you know, Observer keeps a list of its listeners who are subscribed to alerts about certain actions. In this case, if we no longer use a certain class that is a subscriber of this observer, then the GC will not be able to “collect” it, since the link to it is stored in the Observer instance itself.
Singleton
Once a singleton instance has been initialized, it remains in memory for the entire lifetime of the application. As a result, this instance cannot be assembled by the assembler. This pattern should be used only when it is justified by the real requirements for permanent storage in memory.
Threadlocal variables
The link to the ThreadLocal variable is used by the thread associated with it. In most application servers, threads are reused in pools, therefore ThreadLocal data will not be collected by the GC. If the application itself does not care about clearing these values, this will result in a serious memory leak.
Variable static objects
Also, frequent use of memory leaks in Java applications is the misuse of static. A static variable is stored by its class, and as a result, by its loader (classloader). Due to external use, the chance that the garbage collector does not collect this instance increases. Also, information is often cached in static variables or the states used by several threads are stored. A separate example is static collections. A good tone for architectural design is the complete avoidance of variable static objects - there is often a better alternative.
Creating objects
Consider two cases.
Case 1:
Elem e;
e = new HeavyElem();
e = new HeavyElem();
Case 2:
Elem e;
e = new HeavyElem();
e = null;
e = new HeavyElem();
The second case is more correct, since in most cases this will clearly indicate to the collector that the unused instance of the original HeavyElem is collected.
Class loaders
References to classes are used by their loaders and usually are not compiled by the GC until the classloader itself is compiled. This will often occur, for example, in situations with the unloading of applications from the OSGi container. It should also be remembered and take appropriate action.
How to avoid them?
And now for fixing a few tips on how to avoid problems with memory leaks:
1. Use profilers. The profiler helps to see which objects are located on the heap (and also to view them directly in instances), which will allow to catch leaks in the early stages.
2. Be careful about using string operations, especially in cases when the program is working on processing a lot of text data.
3. Always watch carefully if you need non-static inner classes, static variables.
4. Clean up collections of objects after the data has been processed and do not need further use.
Write a good code and do not forget about the rules for handling memory!