The text comes from a popular discussion on the StackOverflow Q&A website: How to write a piece of code in Java that will cause memory leaks.
Q: I just participated in an interview, and the interviewer asked me how to write Java code that causes memory leaks. I have no idea about this question, which is so embarrassing.
A1: Memory leaks can easily occur through the following steps (program code cannot access certain objects, but they are still saved in memory):
The application creates a long-running thread (or uses a thread pool, which will memory leaks occur faster).
The thread loads a class through a certain class loader (can be customized).
This class allocates a large block of memory (such as new byte[1000000]), stores a strong reference in a static variable, and then stores its own reference in ThreadLocal. Allocating additional memory new byte[1000000] is optional (class instance leaks are sufficient), but this will make the memory leak faster.
Threads clean up custom classes or the class loader that loads the class.
Repeat the above steps.
Since there is no reference to the class and class loader, the storage in ThreadLocal cannot be accessed. ThreadLocal holds a reference to the object, and it also holds a reference to the class and its class loader. The class loader holds all references to the classes it loads, so that the GC cannot reclaim the memory stored in ThreadLocal. In many JVM implementations, Java classes and class loaders are directly allocated to the permgen area without performing GC, which leads to more serious memory leaks.
One of the variants of this leak pattern is that if you frequently redeploy applications and application containers (such as Tomcat) that use ThreadLocal in any form, memory leaks will easily occur (because the application container uses threads as mentioned above , a new classloader will be used each time the app is redeployed).
A2:
Static variable reference object
class MemorableClass { static final ArrayList list = new ArrayList(100); }
Call String.intern() of a long string
String str=readString(); // read lengthy string any source db,textbox/jsp etc.. // This will place the string in memory pool from which you cant remove str.intern();
Opened streams (files, networks, etc.) are not closed
try { BufferedReader br = new BufferedReader(new FileReader(inputFile)); ... ... } catch (Exception e) { e.printStacktrace(); }
The connection is not closed
try { Connection conn = ConnectionFactory.getConnection(); ... ... } catch (Exception e) { e.printStacktrace(); }
JVM’s GC is not available Reach area
For example, memory allocated through native method.
Objects of the web application in the application scope, the application has not been restarted or not explicitly removed
getServletContext().setAttribute("SOME_MAP", map);
Objects of the web application in the session scope, have not been invalidated or have not been explicitly removed Remove
session.setAttribute("SOME_MAP", map);
Incorrect or inappropriate JVM options
For example, IBM JDK's noclassgc prevents garbage collection of useless classes
A3: If HashSet is not implemented correctly (or Not implemented) hashCode() or equals() will cause "copies" to continue to be added to the collection. If the collection fails to ignore the elements it should ignore, its size can only continue to grow, and the elements cannot be deleted.
If you want to generate wrong key-value pairs, you can do as follows:
class BadKey { // no hashCode or equals(); public final String key; public BadKey(String key) { this.key = key; } } Map map = System.getProperties(); map.put(new BadKey("key"), "value"); // Memory leak even if your threads die.
A4: In addition to forgotten listeners, static references, key errors/modifications in the hashmap or thread blocking that cannot end the life cycle, etc. Memory leak scenarios, here are some less obvious Java memory leak scenarios, mainly related to threads.
Runtime.addShutdownHook is not removed. Even if removeShutdownHook is used, due to the bug of the ThreadGroup class for unstarted threads, it may not be recycled, causing a memory leak in the ThreadGroup.
Threads are created but not started, the same situation as above
Creating threads that inherit ContextClassLoader and AccessControlContext, the use of ThreadGroup and InheritedThreadLocal, all these references are potential leaks, as well as all classes loaded by the class loader and all Static references and so on. This has a very obvious impact on the ThreadFactory interface as an important component of the entire j.u.c.Executor framework (java.util.concurrent), and many developers have not noticed its potential dangers. And many libraries will start threads according to requests.
ThreadLocal caching is not a good practice in many cases. There are many implementations of simple caching based on ThreadLocal, but the ContextClassLoader will leak if the thread continues to run outside its expected life cycle. Don't use ThreadLocal caching unless really necessary.
ThreadGroup.destroy() is called when ThreadGroup itself has no threads but still has child thread groups. If a memory leak occurs, the thread group cannot be removed from its parent thread group and child thread groups cannot be enumerated.
Using WeakHashMap, value directly (indirectly) refers to key, which is a difficult situation to find. This also applies if a class inheriting Weak/SoftReference may hold a strong reference to the protected object.
Use java.net.URL of http(s) protocol to download resources. KeepAliveCache creates a new thread in the system ThreadGroup, causing the context class loader memory of the current thread to leak. The thread is created on the first request when there is no surviving thread, so a leak is very likely to occur. (This has been fixed in Java 7, the code that creates the thread properly removes the context class loader.)
Use InflaterInputStream to pass new java.util.zip.Inflater() in the constructor (such as PNGImageDecoder) and do not call the end() of the inflater. Just new is very safe, but if you create the class yourself as a constructor parameter and call the close() of the stream and cannot close the inflater, memory leaks may occur. This is not really a memory leak because it will be released by the finalizer. But this consumes a lot of native memory, causing Linux's oom_killer to kill the process. So the lesson this teaches us is: release native resources as early as possible.
The same goes for java.util.zip.Deflater, which is even more serious. The good thing may be that Deflater is rarely used. If you create a Deflater or Inflater yourself, remember that you must call end().