How can I create a memory leak in Java?

后端 未结 30 1773
没有蜡笔的小新
没有蜡笔的小新 2020-11-21 22:26

I just had an interview, and I was asked to create a memory leak with Java.

Needless to say, I felt pretty dumb having no clue on how to eve

相关标签:
30条回答
  • 2020-11-21 22:46

    The answer depends entirely on what the interviewer thought they were asking.

    Is it possible in practice to make Java leak? Of course it is, and there are plenty of examples in the other answers.

    But there are multiple meta-questions that may have been being asked?

    • Is a theoretically "perfect" Java implementation vulnerable to leaks?
    • Does the candidate understand the difference between theory and reality?
    • Does the candidate understand how garbage collection works?
    • Or how garbage collection is supposed to work in an ideal case?
    • Do they know they can call other languages through native interfaces?
    • Do they know to leak memory in those other languages?
    • Does the candidate even know what memory management is, and what is going on behind the scene in Java?

    I'm reading your meta-question as "What's an answer I could have used in this interview situation". And hence, I'm going to focus on interview skills instead of Java. I believe you're more likely to repeat the situation of not knowing the answer to a question in an interview than you are to be in a place of needing to know how to make Java leak. So, hopefully, this will help.

    One of the most important skills you can develop for interviewing is learning to actively listen to the questions and working with the interviewer to extract their intent. Not only does this let you answer their question the way they want, but also shows that you have some vital communication skills. And when it comes down to a choice between many equally talented developers, I'll hire the one who listens, thinks, and understands before they respond every time.

    0 讨论(0)
  • 2020-11-21 22:46

    I can copy my answer from here: Easiest way to cause memory leak in Java?

    "A memory leak, in computer science (or leakage, in this context), occurs when a computer program consumes memory but is unable to release it back to the operating system." (Wikipedia)

    The easy answer is: You can't. Java does automatic memory management and will free resources that are not needed for you. You can't stop this from happening. It will ALWAYS be able to release the resources. In programs with manual memory management, this is different. You cann get some memory in C using malloc(). To free the memory, you need the pointer that malloc returned and call free() on it. But if you don't have the pointer anymore (overwritten, or lifetime exceeded), then you are unfortunately incapable of freeing this memory and thus you have a memory leak.

    All the other answers so far are in my definition not really memory leaks. They all aim at filling the memory with pointless stuff real fast. But at any time you could still dereference the objects you created and thus freeing the memory --> NO LEAK. acconrad's answer comes pretty close though as I have to admit since his solution is effectively to just "crash" the garbage collector by forcing it in an endless loop).

    The long answer is: You can get a memory leak by writing a library for Java using the JNI, which can have manual memory management and thus have memory leaks. If you call this library, your java process will leak memory. Or, you can have bugs in the JVM, so that the JVM looses memory. There are probably bugs in the JVM, there may even be some known ones since garbage collection is not that trivial, but then it's still a bug. By design this is not possible. You may be asking for some java code that is effected by such a bug. Sorry I don't know one and it might well not be a bug anymore in the next Java version anyway.

    0 讨论(0)
  • 2020-11-21 22:47

    I recently encountered a memory leak situation caused in a way by log4j.

    Log4j has this mechanism called Nested Diagnostic Context(NDC) which is an instrument to distinguish interleaved log output from different sources. The granularity at which NDC works is threads, so it distinguishes log outputs from different threads separately.

    In order to store thread specific tags, log4j's NDC class uses a Hashtable which is keyed by the Thread object itself (as opposed to say the thread id), and thus till the NDC tag stays in memory all the objects that hang off of the thread object also stay in memory. In our web application we use NDC to tag logoutputs with a request id to distinguish logs from a single request separately. The container that associates the NDC tag with a thread, also removes it while returning the response from a request. The problem occurred when during the course of processing a request, a child thread was spawned, something like the following code:

    pubclic class RequestProcessor {
        private static final Logger logger = Logger.getLogger(RequestProcessor.class);
        public void doSomething()  {
            ....
            final List<String> hugeList = new ArrayList<String>(10000);
            new Thread() {
               public void run() {
                   logger.info("Child thread spawned")
                   for(String s:hugeList) {
                       ....
                   }
               }
            }.start();
        }
    }    
    

    So an NDC context was associated with inline thread that was spawned. The thread object that was the key for this NDC context, is the inline thread which has the hugeList object hanging off of it. Hence even after the thread finished doing what it was doing, the reference to the hugeList was kept alive by the NDC context Hastable, thus causing a memory leak.

    0 讨论(0)
  • 2020-11-21 22:47

    The interviewer might have be looking for a circular reference solution:

        public static void main(String[] args) {
            while (true) {
                Element first = new Element();
                first.next = new Element();
                first.next.next = first;
            }
        }
    

    This is a classic problem with reference counting garbage collectors. You would then politely explain that JVMs use a much more sophisticated algorithm that doesn't have this limitation.

    -Wes Tarle

    0 讨论(0)
  • 2020-11-21 22:48

    Take any web application running in any servlet container (Tomcat, Jetty, Glassfish, whatever...). Redeploy the app 10 or 20 times in a row (it may be enough to simply touch the WAR in the server's autodeploy directory.

    Unless anybody has actually tested this, chances are high that you'll get an OutOfMemoryError after a couple of redeployments, because the application did not take care to clean up after itself. You may even find a bug in your server with this test.

    The problem is, the lifetime of the container is longer than the lifetime of your application. You have to make sure that all references the container might have to objects or classes of your application can be garbage collected.

    If there is just one reference surviving the undeployment of your web app, the corresponding classloader and by consequence all classes of your web app cannot be garbage collected.

    Threads started by your application, ThreadLocal variables, logging appenders are some of the usual suspects to cause classloader leaks.

    0 讨论(0)
  • 2020-11-21 22:50

    Here's a good way to create a true memory leak (objects inaccessible by running code but still stored in memory) in pure Java:

    1. The application creates a long-running thread (or use a thread pool to leak even faster).
    2. The thread loads a class via an (optionally custom) ClassLoader.
    3. The class allocates a large chunk of memory (e.g. new byte[1000000]), stores a strong reference to it in a static field, and then stores a reference to itself in a ThreadLocal. Allocating the extra memory is optional (leaking the class instance is enough), but it will make the leak work that much faster.
    4. The application clears all references to the custom class or the ClassLoader it was loaded from.
    5. Repeat.

    Due to the way ThreadLocal is implemented in Oracle's JDK, this creates a memory leak:

    • Each Thread has a private field threadLocals, which actually stores the thread-local values.
    • Each key in this map is a weak reference to a ThreadLocal object, so after that ThreadLocal object is garbage-collected, its entry is removed from the map.
    • But each value is a strong reference, so when a value (directly or indirectly) points to the ThreadLocal object that is its key, that object will neither be garbage-collected nor removed from the map as long as the thread lives.

    In this example, the chain of strong references looks like this:

    Thread object → threadLocals map → instance of example class → example class → static ThreadLocal field → ThreadLocal object.

    (The ClassLoader doesn't really play a role in creating the leak, it just makes the leak worse because of this additional reference chain: example class → ClassLoader → all the classes it has loaded. It was even worse in many JVM implementations, especially prior to Java 7, because classes and ClassLoaders were allocated straight into permgen and were never garbage-collected at all.)

    A variation on this pattern is why application containers (like Tomcat) can leak memory like a sieve if you frequently redeploy applications which happen to use ThreadLocals that in some way point back to themselves. This can happen for a number of subtle reasons and is often hard to debug and/or fix.

    Update: Since lots of people keep asking for it, here's some example code that shows this behavior in action.

    0 讨论(0)
提交回复
热议问题