奇怪的内存泄漏

3

编辑:我认为解析XML的代码中存在逻辑错误,如果正确编写代码,则不应该有任何泄漏。

所以我添加了一个AsyncTask类到我的项目中,这个类是我之前为快速测试编写的,但是它导致了内存泄漏错误。

我确定它是有责任的,因为当我删除它时,泄漏问题就消失了。

这里是堆转储文件(已经转换)http://www7.zippyshare.com/v/83628026/file.html

package com.example.xmldl;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.os.AsyncTask;
import android.util.Xml;

public class Dlxml extends AsyncTask<String, Void, Void> {
    //adding or removing Override didnt matter just to let you know
    InputStream istream = null;
    protected Void doInBackground(String... url){

        try {
            downloadXml(url[0]);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

    private void downloadXml(String url) throws IOException {   
        try {
            URL mUrl = new URL(url);
            HttpURLConnection urlConnection = (HttpURLConnection) mUrl.openConnection();
            istream = urlConnection.getInputStream();           
            XmlPullParser parser = Xml.newPullParser();
            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
            parser.setInput(istream, null);
            parser.nextTag();
            parser.require(XmlPullParser.START_TAG, null, "resources");
            while (parser.next() != XmlPullParser.END_TAG) {
                if (parser.getEventType() == XmlPullParser.TEXT) {                  
                    parser.nextTag();                   
                }
                if (parser.getEventType() != XmlPullParser.START_TAG) {
                    continue;
                }               
            }           
        } catch (MalformedURLException e) {
            e.printStackTrace();        
        } catch (XmlPullParserException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        finally {
            istream.close();            
        }

    }

}

实际的错误信息是什么? - Kerrek SB
如果您能截屏转储视图,而不是强制我们在当前计算机上安装MemAnalyzer,则会更简单。谢谢。 - AlexN
@AlexN 你想让我截取哪个具体页面的屏幕截图? - FireFly
3个回答

0

我不相信你正在关闭你的输入流:

private void downloadXml(String url) throws IOException {   
    try {
        URL mUrl = new URL(url);
        HttpURLConnection urlConnection = (HttpURLConnection) mUrl.openConnection();
        InputStream istream = urlConnection.getInputStream();           
        XmlPullParser parser = Xml.newPullParser();
        parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
        parser.setInput(istream, null);
        parser.nextTag();
        parser.require(XmlPullParser.START_TAG, null, "resources");
        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() == XmlPullParser.TEXT) {                  
                parser.nextTag();                   
            }
            if (parser.getEventType() != XmlPullParser.START_TAG) {
                continue;
            }               
        }  
        // dont forget to close the input stream!
        istream.close();
    } catch (IOException e) {
        //error closing istream
        e.printStackTrace();        
    } catch (MalformedURLException e) {
        e.printStackTrace();        
    } catch (XmlPullParserException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    finally {

    }

}

哦,抱歉我在finally中关闭了inputstream。不知怎么地,我在复制时将其删除了。 - FireFly

0
在这段代码中,我看不到可能存在内存泄漏的问题。因此,问题很可能出在所包含的代码中。由于我不熟悉XmlPullParser,问题可能就在那里。无论如何,输入流应该在finally语句块中关闭,因此你需要修改你的代码。
finally {
            istream.close();            
        }

不好 :)

finally {
      if(istream != null){
            istream.close();            
       }
}

更好 :)


这就是为什么这个内存泄漏很奇怪,因为只有很少的代码会导致内存泄漏。但它确实是一个完整的内存泄漏 :/ 顺便说一句:我确定是这个类导致了内存泄漏,因为如果尝试从另一个 Activity 执行它,内存泄漏就像地狱一样涌现。 - FireFly

0
在 Android 中,大部分的内存泄漏往往与线程有关。所以,我首先要问您如何调用这个类(Dlxml,继承自 AsyncTask)。请确保您没有使用匿名变量,比如“new Dlxml().execute()”,因为 Android 的垃圾回收器可能需要很长时间才能检测到线程不再使用(如果您在列表中使用加载器,则可能会导致大量泄漏)。在实现并行处理之前,我建议您检查以下两点:
  • 始终使用静态变量来实例化加载器。这有助于防止泄漏,并且是 Android 警告注释。尝试在线程和处理程序问题上使用静态类变量;
  • 注意使用哪个线程调用更改视图的方法。如果在一个不是主线程的线程中调用更改视图的方法,logcat 将抛出一个关于泄露的消息。为此,请使用 activity 的 runonuithread 方法;

重要提示:我发现您已经使用了 AsyncTask,并且只在后台做了很多事情。这些事情中有什么会改变视图吗?您可以在 doInBackground 中进行下载,在 onPostExecute 中更改视图。

您可能也对以下链接感兴趣:


我正在使用匿名变量,所以我会立即更改它以查看是否修复了问题。目前我还没有发布任何内容,这个任务是将一些日志消息发布到logcat中,但为了可读性我已经将其删除。您能否给出第一个建议的代码示例? - FireFly
我按照你的建议修改了代码,但它并没有阻止它:Dlxml getXml = new Dlxml(); getXml.execute(mUrl); //其中mUrl是一个静态变量 - FireFly
正如matheszabi所说,很难(如果它真的存在)在这段代码片段中看到内存泄漏。还有一件事要问:您是否使用某些方法来关闭连接?我通常会看到以下这三个步骤来关闭Web服务请求:
  • httpPost.abort();
  • closeInputStream();
  • httpClient.getConnectionManager().shutdown(); 希望这能在某种程度上帮助您... :)
- Marcelo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接