用 Java 创建内存泄漏

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 even start creating one.

What would an example be?

转载于:https://stackoverflow.com/questions/6470651/creating-a-memory-leak-with-java

csdnceshi52
妄徒之命 Google: "Java memory leak"
2 年多之前 回复
csdnceshi64
游.程 Leaving a Scanner unclosed :P
大约 3 年之前 回复
csdnceshi69
YaoRaoLov infinite recurrent method will blow the stack which technically is a memory leak?
大约 3 年之前 回复
csdnceshi77
狐狸.fox I've been asked this question in an interview and the interviewer really wanted to know what can cause the JVM to run out of heap space or perm gen space. I think its a poorly worded interview question in my opinion. I would first qualify: do they really want you to understand errors related to running out of memory and to diagnose and fix them? Trying to provide a sophisticated example to guarantee a problem across all JVMs (some that have deadlock detection) and configurations (a ton of memory allocated) might not be what they are truely looking for.
3 年多之前 回复
csdnceshi59
ℙℕℤℝ Just upvoted so the counter equals the current year. gj mates
3 年多之前 回复
csdnceshi60
℡Wang Yan add data to static lists, create new processes from your JVM, create file handle leaks (they consume memory as well, though usually your OS dies well before it runs out of memory), ... I think the question is more "when is a memory leak a memory leak" than "how do you create one".
接近 4 年之前 回复
weixin_41568126
乱世@小熊 Use a LoggerFactory to create a new logger having a name generated by an incremental sequence. Logger logger = LoggerFactory.get(i); where i increments.
4 年多之前 回复
csdnceshi56
lrony* If you are using any database related tasks then it will be easy to create a memory leak just by ignoring the dispose of the database connection after you finish your database job. such as ignoring to write conn.close() after you opened the connection in a method or a class. You can see the performance of the application will be so weak due to memory leak.
4 年多之前 回复
csdnceshi66
必承其重 | 欲带皇冠 Heck... Java itself is a memory leak. Is running nothing in Java not enough?
接近 5 年之前 回复
weixin_41568127
?yb? you can have a reference leak - a long running thread creates a list collection and always adds objects to it, as long as there is a reference to this list it stays in memory and it grows and grows. How to fix it? java heap profiler hprof can help with visualizing memory usage of threads.
大约 6 年之前 回复
csdnceshi50
三生石@ What about using JNI and make a memory leak in your native code?
接近 8 年之前 回复
csdnceshi71
Memor.の - isn't that a leak in the JVM, not in the Java code? Memory leaks don't exist in interpreted code, but they can exist in the code doing the interpreting.
大约 8 年之前 回复
csdnceshi80
胖鸭 I have encountered one issue in tomcat 5.5, 6.0 and later i came to know this is a memory leak. The following Question itself will give you how to create a memory leak in permgen generation stackoverflow.com/q/10937935/639891
8 年多之前 回复
weixin_41568110
七度&光 while I consider installing JDBC drivers inside the web-app a terribly bad practice, the containers are free to uninstall any JDBC driver and it takes around 3 lines of code. Tomcat does unregistered for quite long time. Yet, still a leak: if you install anything, you must uninstall it - leak.
大约 9 年之前 回复
csdnceshi54
hurriedly% most applications that have a JDBC driver inside the webapp (like most app that want to be independant of container datasource) forget to unregister the JDBC driver when unloading. This one of the root cause for memory leak when unloading app in Toncat. And Tomcat can now FORCE the deregistering the driver (wiki.apache.org/tomcat/MemoryLeakProtection#jdbc). The leak do appear when unloading, not before so you basically mesure the JDBC unregister bug, and not the application leaks.
9 年多之前 回复
weixin_41568110
七度&光 your statement aint true for the simple reason: if the JDBC driver was loaded by the same classloader that loads the application and effectively remains registered, the classloader and all classes will be leaking just the same. If a simple class or instance pertains a registration is a leak clear as day. (in other words failing to unregister or the register not using weak references - leak)
9 年多之前 回复
csdnceshi54
hurriedly% Not to be offencive. But do you really think at your BeanShell solution like that something you'll want to do, something pratical?
9 年多之前 回复
csdnceshi54
hurriedly% Not really. In fact the appliction could really never leak in production but fail to unregister some things (like JDBC drivers) when unloading. This is anoying, but not that bad as you can stay month without redeploy and without leaks. The thing is that as it not pratical to restart your server each time you redeploy, it is worse to have to redeploy the app to recover memory. (And after an OutOfMemory exception you'll not have the opportunity anyway).
9 年多之前 回复
csdnceshi51
旧行李 Not necessarily, if your application uses something like BeanShell or any sort of dynamic loading, you can cause new code to run to fix the problem without recompiling. Obviously a root fix is always preferable, but in some cases it's as easy as triggering an already-written code path to periodically clean things up.
9 年多之前 回复
weixin_41568110
七度&光 defining a special class of leaks is quite easy to me when you have pluggable/redeployable modules, if after a redeploy and full garbage collection a previously loaded class instance remains, it's a leak. Of course, there could be a lot other type like String, byte[] and what not leaks but they manifest themselves too.
9 年多之前 回复
csdnceshi67
bug^君 (1) "ability" to reclaim the memory is useless if your program never use that ability; on both cases you have to change your source code to do the Right Thing. (2) you can even more easily detect the "true memory leaks" because a tool like Valgrind can tell for sure that the memory is completely lost (so, yes, that's another difference). (3) That's true. But that's why I said "equivalent" not "completely the same situation, without any detectable difference".
9 年多之前 回复
csdnceshi51
旧行李 No, they are not equivalent. (1) You possess the ability to reclaim the memory, whereas in a "true leak" your C/C++ program forgets the range that was allocated, there's no safe way to recover. (2) You can very easily detect the problem with profiling, because you can see what objects the "bloat" involves. (3) A "true leak" is an unequivocal error, while a program that keeps lots of objects around until terminated could be a deliberate part of how it is meant to work.
9 年多之前 回复
csdnceshi51
旧行李 It was linked on Reddit, so that's probably driven a lot of views. (And of course, everybody there is having the same problem with the distinction between a memory leak versus just cruddy program logic.)
9 年多之前 回复
csdnceshi61
derek5. - darien's answer is not fanboyism. he explicitly admitted that certain JVMs can have bugs that mean memory gets leaked. this is different than the language spec itself allowing for memory leaks.
9 年多之前 回复
csdnceshi68
local-host Honestly I can't believe the similar question I asked about "Go" got downvoted to -1. Here: stackoverflow.com/questions/4400311/… Basically the memory leaks I was talking about are the ones who got +200 upvotes to the OP and yet I got attacked and insulted for asking if "Go" had the same issue. Somehow I'm not sure that all wiki-thing is working that great.
9 年多之前 回复
csdnceshi67
bug^君 I find it funny that on most answers people are looking for those edge cases and tricks and seem to be completely missing the point (IMO). They could just show code that keep useless references to objects that will never use again, and at the same time never drop those references; one may say those cases are not "true" memory leaks because there are still references to those objects around, but if the program never use those references again and also never drop them, it is completely equivalent to (and as bad as) a "true memory leak".
9 年多之前 回复
csdnceshi65
larry*wei Do they mean, explicitly create a memory leak? Or over time, how can a memory leak occur?
9 年多之前 回复
csdnceshi51
旧行李 I would tell them that Java uses a garbage collector, and ask them to be a bit more specific about their definition of "memory leak", explaining that--barring JVM bugs--Java can't leak memory in quite the same way C/C++ can. You have to have a reference to the object somewhere.
9 年多之前 回复
weixin_41568110
七度&光 I guess I can add at least 10 extra weird cases of memory leaks (my 2nd specialty feels tracking such issues)
9 年多之前 回复
csdnceshi78
程序go I had forgotten about File.deleteOnExit(), that's a great one. If you post that as an answer, I'll upvote it.
9 年多之前 回复

30个回答

Here's a simple/sinister one via http://wiki.eclipse.org/Performance_Bloopers#String.substring.28.29.

public class StringLeaker
{
    private final String muchSmallerString;

    public StringLeaker()
    {
        // Imagine the whole Declaration of Independence here
        String veryLongString = "We hold these truths to be self-evident...";

        // The substring here maintains a reference to the internal char[]
        // representation of the original string.
        this.muchSmallerString = veryLongString.substring(0, 1);
    }
}

Because the substring refers to the internal representation of the original, much longer string, the original stays in memory. Thus, as long as you have a StringLeaker in play, you have the whole original string in memory, too, even though you might think you're just holding on to a single-character string.

The way to avoid storing an unwanted reference to the original string is to do something like this:

...
this.muchSmallerString = new String(veryLongString.substring(0, 1));
...

For added badness, you might also .intern() the substring:

...
this.muchSmallerString = veryLongString.substring(0, 1).intern();
...

Doing so will keep both the original long string and the derived substring in memory even after the StringLeaker instance has been discarded.

weixin_41568126
乱世@小熊 You don't even need to do the substring() yourself: Use a regex matcher to match a tiny part of a huge input, and carry the "extracted" string around for a long time. The huge input stays alive up to Java 6.
大约 3 年之前 回复
weixin_41568174
from.. The method substring() creates a new String in java7 (it is a new behavior)
7 年多之前 回复
weixin_41568134
MAO-EYE that's a fair point. The non-intern case may be more of a "memory surprise" than a "memory leak." .intern()ing the substring, though, certainly creates a situation where the reference to the longer string is preserved and cannot be freed.
9 年多之前 回复
csdnceshi58
Didn"t forge I wouldn't call that a memory leak, per se. When muchSmallerString is freeed (because the StringLeaker object is destroyed), the long string will be freed as well. What I call memory leak is memory that can never been freeed in this instance of JVM. However, you have shown yourself how to free the memory: this.muchSmallerString=new String(this.muchSmallerString). With a real memory leak, there is nothing you can do.
9 年多之前 回复

Maybe by using external native code through JNI?

With pure Java, it is almost impossible.

But that is about a "standard" type of memory leak, when you cannot access the memory anymore, but it is still owned by the application. You can instead keep references to unused objects, or open streams without closing them afterwards.

csdnceshi57
perhaps? there are basically +400 upvotes on various answers by people with +10 000 rep showing that in both the cases Joachim Sauer commented it's very possible. So your "almost impossible" makes no sense.
9 年多之前 回复
weixin_41568134
MAO-EYE "With pure java, it is almost impossible." Well, my experience is another especially when it comes to implementing caches by people that are not aware of the pitfalls here.
9 年多之前 回复
csdnceshi69
YaoRaoLov Sauer - I meant the second type. The first is fairly easy to make :)
9 年多之前 回复
csdnceshi76
斗士狗 That depends on the definition of "memory leak". If "memory that's held on to, but no longer needed", then it's easy to do in Java. If it's "memory that's allocated but not accessible by the code at all", then it gets slightly harder.
9 年多之前 回复

I have had a nice "memory leak" in relation to PermGen and XML parsing once. The XML parser we used (I can't remember which one it was) did a String.intern() on tag names, to make comparison faster. One of our customers had the great idea to store data values not in XML attributes or text, but as tagnames, so we had a document like:

<data>
   <1>bla</1>
   <2>foo</>
   ...
</data>

In fact, they did not use numbers but longer textual IDs (around 20 characters), which were unique and came in at a rate of 10-15 million a day. That makes 200 MB of rubbish a day, which is never needed again, and never GCed (since it is in PermGen). We had permgen set to 512 MB, so it took around two days for the out-of-memory exception (OOME) to arrive...

csdnceshi70
笑故挽风 Note that this is no longer true for JDK 7+, where String interning happens on the heap. See this article for a detailed writeup: java-performance.info/string-intern-in-java-6-7-8
3 年多之前 回复
csdnceshi66
必承其重 | 欲带皇冠 I guess you are right. This was just for demonstration.
9 年多之前 回复
csdnceshi74
7*4 Just to nitpick your example code: I think numbers (or strings starting with numbers) are not allowed as element names in XML.
9 年多之前 回复

As a lot of people have suggested, Resource Leaks are fairly easy to cause - like the JDBC examples. Actual Memory leaks are a bit harder - especially if you aren't relying on broken bits of the JVM to do it for you...

The ideas of creating objects that have a very large footprint and then not being able to access them aren't real memory leaks either. If nothing can access it then it will be garbage collected, and if something can access it then it's not a leak...

One way that used to work though - and I don't know if it still does - is to have a three-deep circular chain. As in Object A has a reference to Object B, Object B has a reference to Object C and Object C has a reference to Object A. The GC was clever enough to know that a two deep chain - as in A <--> B - can safely be collected if A and B aren't accessible by anything else, but couldn't handle the three-way chain...

csdnceshi58
Didn"t forge Hasn't been the case for some time now. Modern GCs know how to handle circular references.
7 年多之前 回复

The interviewer was probably looking for a circular reference like the code below (which incidentally only leak memory in very old JVMs that used reference counting, which isn't the case any more). But it's a pretty vague question, so it's a prime opportunity to show off your understanding of JVM memory management.

class A {
    B bRef;
}

class B {
    A aRef;
}

public class Main {
    public static void main(String args[]) {
        A myA = new A();
        B myB = new B();
        myA.bRef = myB;
        myB.aRef = myA;
        myA=null;
        myB=null;
        /* at this point, there is no access to the myA and myB objects, */
        /* even though both objects still have active references. */
    } /* main */
}

Then you can explain that with reference counting, the above code would leak memory. But most modern JVMs don't use reference counting any longer, most use a sweep garbage collector, which will in fact collect this memory.

Next you might explain creating an Object that has an underlying native resource, like this:

public class Main {
    public static void main(String args[]) {
        Socket s = new Socket(InetAddress.getByName("google.com"),80);
        s=null;
        /* at this point, because you didn't close the socket properly, */
        /* you have a leak of a native descriptor, which uses memory. */
    }
}

Then you can explain this is technically a memory leak, but really the leak is caused by native code in the JVM allocating underlying native resources, which weren't freed by your Java code.

At the end of the day, with a modern JVM, you need to write some Java code that allocates a native resource outside the normal scope of the JVM's awareness.

A common example of this in GUI code is when creating a widget/component and adding a listener to some static/application scoped object and then not removing the listener when the widget is destroyed. Not only do you get a memory leak, but also a performance hit as when whatever you are listening to fires events, all your old listeners are called too.

weixin_41568196
撒拉嘿哟木头 The android platform gives the exemple of a memory leak created by caching a Bitmap in the static field of a View.
9 年多之前 回复

I thought it was interesting that no one used the internal class examples. If you have an internal class; it inherently maintains a reference to the containing class. Of course it is not technically a memory leak because Java WILL eventually clean it up; but this can cause classes to hang around longer than anticipated.

public class Example1 {
  public Example2 getNewExample2() {
    return this.new Example2();
  }
  public class Example2 {
    public Example2() {}
  }
}

Now if you call Example1 and get an Example2 discarding Example1, you will inherently still have a link to an Example1 object.

public class Referencer {
  public static Example2 GetAnExample2() {
    Example1 ex = new Example1();
    return ex.getNewExample2();
  }

  public static void main(String[] args) {
    Example2 ex = Referencer.GetAnExample2();
    // As long as ex is reachable; Example1 will always remain in memory.
  }
}

I've also heard a rumor that if you have a variable that exists for longer than a specific amount of time; Java assumes that it will always exist and will actually never try to clean it up if cannot be reached in code anymore. But that is completely unverified.

csdnceshi58
Didn"t forge The "rumor" sounds like someone half-read about how generational GC works. Long-lived-but-now-unreachable objects can indeed stick around and take up space for a while, because the JVM promoted them out of the younger generations so it could stop checking them every pass. They will evade the piddly "clean up my 5000 temp strings" passes, by design. But they're not immortal. They're still eligible for collection, and if the VM is strapped for RAM, it will eventually run a full GC sweep and repossess that memory.
大约 7 年之前 回复
csdnceshi80
胖鸭 inner classes are rarely an issue. They are a straightforward case and very easy to detect. The rumor is just a rumor too.
9 年多之前 回复

I don't think anyone has said this yet: you can resurrect an object by overriding the finalize() method such that finalize() stores a reference of this somewhere. The garbage collector will only be called once on the object so after that the object will never destroyed.

weixin_41568134
MAO-EYE This answer is misleading, the finalize() method can only be called once by the JVM, but this does not mean that it cannot be re-garbage collected if the object is resurrected and then dereferenced again. If there is resource closing code in the finalize() method then this code will not get run again, this may cause a memory leak.
接近 8 年之前 回复
weixin_41568183
零零乙 This is untrue. finalize() will not be called but the object will be collected once there won't be more references. Garbage collector is not 'called' either.
9 年多之前 回复

You can create a moving memory leak by creating a new instance of a class in that class's finalize method. Bonus points if the finalizer creates multiple instances. Here's a simple program that leaks the entire heap in sometime between a few seconds and a few minutes depending on your heap size:

class Leakee {
    public void check() {
        if (depth > 2) {
            Leaker.done();
        }
    }
    private int depth;
    public Leakee(int d) {
        depth = d;
    }
    protected void finalize() {
        new Leakee(depth + 1).check();
        new Leakee(depth + 1).check();
    }
}

public class Leaker {
    private static boolean makeMore = true;
    public static void done() {
        makeMore = false;
    }
    public static void main(String[] args) throws InterruptedException {
        // make a bunch of them until the garbage collector gets active
        while (makeMore) {
            new Leakee(0).check();
        }
        // sit back and watch the finalizers chew through memory
        while (true) {
            Thread.sleep(1000);
            System.out.println("memory=" +
                    Runtime.getRuntime().freeMemory() + " / " +
                    Runtime.getRuntime().totalMemory());
        }
    }
}

I think that a valid example could be using ThreadLocal variables in an environment where threads are pooled.

For instance, using ThreadLocal variables in Servlets to communicate with other web components, having the threads being created by the container and maintaining the idle ones in a pool. ThreadLocal variables, if not correctly cleaned up, will live there until, possibly, the same web component overwrites their values.

Of course, once identified, the problem can be solved easily.

共30条数据 首页 1 3
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问
相关内容推荐