简体   繁体   English

我已经“修复”了内存泄漏,但是......如何以更好的方式修复它?

[英]I've “fixed” a memory leak, but.. how to fix it in a better way?

It was a very fast and makeshift, bug fix.. It worked, but I would like to find a better understanding and solution. 这是一个非常快速和临时的错误修复..它有效,但我想找到一个更好的理解和解决方案。

This was the Class Constructor generating the leak 这是生成泄漏的类构造函数

final transient  DataInputStream din;
final transient  DataOutputStream dout;
final transient  BufferedReader bin;
final transient  BufferedWriter bout;

NData(Socket sock0) throws IOException
    {
        sock=sock0;
        din= new DataInputStream(sock.getInputStream());
        dout = new DataOutputStream(sock.getOutputStream());
        bin = new BufferedReader(new InputStreamReader(din));
        bout = new BufferedWriter(new OutputStreamWriter(dout));
     //   .. 
    }

The bug fix was to changed it (remove final) so that let me to assign null later 错误修复是更改它(删除最终),以便让我稍后分配null

transient  DataInputStream din;
transient  DataOutputStream dout;
transient  BufferedReader bin;
transient  BufferedWriter bout;

NData(Socket sock0) throws IOException
    {
        sock=sock0;
        din= new DataInputStream(sock.getInputStream());
        dout = new DataOutputStream(sock.getOutputStream());
        bin = new BufferedReader(new InputStreamReader(din));
        bout = new BufferedWriter(new OutputStreamWriter(dout));
     //   .. 
    } 

 //And to add a "magic" destructor

 void nuller() {
        din=null;
        dout=null;
        bin=null;
        bout=null;
    }

There was a finish method that ended the thread, closes the streams, so I add there the "nuller" method call, and memory leak went away. 有一个finish方法结束了线程,关闭了流,所以我添加了“nuller”方法调用,内存泄漏消失了。

Why after finished the thread and closed the stream, it keep allocating memory in "byte[]" ? 为什么在完成线程并关闭流后,它会继续在“byte []”中分配内存? Why GC don't throw it away ? 为什么GC不扔掉它? (except after that dirty with null asignment) (除了那些带有空对齐的脏之外)

EDIT: 编辑:

As casablanca says perhaps the NData object is still around, there is a 正如卡萨布兰卡所说也许NData对象仍然存在,有一个

final static ConcurrentHashMap <String,NData>(); 

so that have NData as values, A remove(key) is done to purge the object from the Map, but.. it seems not be enough. 因此,将NData作为值,使用remove(key)从Map中清除对象,但是......似乎还不够。

Removing from a HashMap the only object reference won't be enough to remove the object? 从HashMap中删除唯一的对象引用是不足以删除对象?

Why after finished the thread and closed the stream, it keep allocating memory in "byte[]" ? 为什么在完成线程并关闭流后,它会继续在“byte []”中分配内存? Why GC don't throw it away ? 为什么GC不扔掉它?

The GC will "throw" something away only when there are no more references to that object. 只有在没有对该对象的引用时,GC才会“扔掉”某些内容。 In your case, this means that something is still holding a reference to the NData object. 在您的情况下,这意味着某些东西仍然持有对NData对象的引用。 By manually calling your nuller method, you simply release the references to the member variables ( din , dout etc.) but the NData object is probably still lying around. 通过手动调用你的nuller方法,你只需释放对成员变量的引用( dindout等),但NData对象可能仍然存在。 You need to look elsewhere to find out who is using this object and make sure that this reference is cleaned up. 您需要查看其他地方以找出谁正在使用此对象并确保清除此引用。

Update: How exactly did you come to the conclusion that there is a memory leak? 更新:您究竟是如何得出内存泄漏的结论? The GC only runs periodically, so objects are not guaranteed to be freed immediately. GC仅定期运行,因此不保证立即释放对象。 You could try calling System.gc() to force a GC run. 您可以尝试调用System.gc()来强制运行GC。 Also, ConcurrentHashMap (which I'm not familiar with) might be caching references for concurrency and it's possible that these aren't freed immediately after calling remove . 此外, ConcurrentHashMap (我不熟悉)可能是缓存并发的引用,并且可能在调用remove后不会立即释放它们。

You should take a heap dump after you cleaned up the NData object. 清理NData对象后,应该进行堆转储。 Then find the NData object in the heap (because from your description, someone is still holding a strong reference to it!). 然后在堆中找到NData对象(因为从你的描述中,有人仍然持有它的强引用!)。 Then find how is this object reachable from the root set . 然后找到该对象如何从根集到达。

Recently I had to do this, and I found the process convenient using these tools: 最近我不得不这样做,我发现这个过程很方便:

VisualVM , for taking the heap dump. VisualVM ,用于进行堆转储。

Eclipse Memory Analyzer (I used it standalone) to analyze the heap, in particular find the paths from the root set. Eclipse Memory Analyzer (我单独使用它)来分析堆,特别是从根集中查找路径。

The second currently is significantly more advanced than the first, but the GUI of the first is very nice to use (if it was also feature-complete!). 第二个当前比第一个更先进,但第一个的GUI非常好用(如果它也是功能完整的!)。

In your nuller call bin.close() and bout.close() That will do it. 在你的空调中,调用bin.close()和bout.close()就可以了。 Just make sure that the nuller is called in a finally block somewhere so it always gets called. 只需确保在某个地方的finally块中调用nuller,以便始终调用它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM