使用inputStream.available()的Java/Android SDK内存泄漏

使用inputStream.available()的Java/Android SDK内存泄漏,java,android,performance,Java,Android,Performance,我编写了一个Java类,它有一个现有tcp连接池和一个在这些连接上循环的工作线程。在检查是否需要处理来自对等方的信息的函数中,它调用inputStream.available()。我在看记忆追踪器,它一直在上升。当我记录分配时,有几百个android.util.MutableInt对象分配在 at libcore.io.IoBridge.available(IoBridge.java:57) at java.net.PlainSocketImpl.available(PlainSocketImp

我编写了一个Java类,它有一个现有tcp连接池和一个在这些连接上循环的工作线程。在检查是否需要处理来自对等方的信息的函数中,它调用inputStream.available()。我在看记忆追踪器,它一直在上升。当我记录分配时,有几百个android.util.MutableInt对象分配在

at libcore.io.IoBridge.available(IoBridge.java:57)
at java.net.PlainSocketImpl.available(PlainSocketImpl.java:128)
at java.net.PlainSocketImpl$PlainSocketInputStream.available(PlainSocketImpl.java:225)
at myClass
问题代码:

void someFunction() {
    (new Thread(){
        public void run() {
            try {
                while(true) {
                    for(int i=0; i<peers.size(); i++) {
                        peer = peers.get(i);
                        peer.doWorkListen();
                        peer.doWorkSend();
                    }
                    Thread.sleep(1);
                }
            catch (Exception e) {}
        }).start();
}

void peer.doWorkListen() {
    if(inputStream.available() > 0) {
        //Read and process input
    }
}
void someFunction(){
(新线程(){
公开募捐{
试一试{
while(true){
对于(int i=0;i 0){
//读取并处理输入
}
}

您遇到的不是内存泄漏,而是垃圾收集器无法跟上每秒创建的大量垃圾对象。*最终,当gc调用stop the world技术大规模删除不需要的对象时,您会发现应用程序暂时冻结

从您的代码中,我可以看到您正试图使用一个线程从多个流中轮询可用性。我认为您在这里创建的是Java/Android非阻塞I/O的一个非常基本且低效的实现

给定堆栈跟踪,我还假设您在本例中使用套接字。使用
ServerSocketChannel
SocketChannel
以及
选择器
可以为您提供所需的功能。下面提供了一个仅使用选择器和SocketChannel的示例实现(如果这是服务器端代码,则应使用相同的逻辑实现
ServerSocketChannel

public类Foo实现Runnable{
私人最终选择权;
私钥;
public Foo()引发IOException{
选择器=selector.open();
run=true;
}
公共无效注册表通道(SocketChannel通道)引发IOException{
信道配置阻塞(假);
//也可以选择使用选择键进行写入
通道寄存器(选择器,选择键,操作读取);
}
公共空间关闭(){
运行=错误;
selector.wakeup();
试一试{
selector.close();
}捕获(IOException忽略){}
}
公开募捐{
while(运行){
试一试{
int readyCount=selector.select();
//选择器被中断或手动唤醒
如果(readyCount==0){
//妥善处理
}否则{
迭代器iterKeys=selector.selectedKeys().Iterator();
while(iterKeys.hasNext()){
SelectionKey=iterKeys.next();
if(key.isReadable()){
SocketChannel chn=(SocketChannel)key.channel();
//在这里处理输入
}否则{
//我们对不可读的通道不感兴趣
}
//非常重要
iterKeys.remove();
}
}
}捕获(IOEX异常){
//句柄选择器异常
}
}
}
}
Foo
充当您创建的线程的可运行线程,并以有效的方式多路复用
SocketChannel
阻止(与快速轮询相反)套接字,直到有一个可用。这不仅消除了大规模对象创建的问题,还消除了使用
可用()
**这在不同的平台上可能不可靠。这种设计模式还有一个额外的好处,即可能比使用大量套接字的方法更快,因为它不必按顺序遍历套接字列表

查看和上的教程以及,以获得更深入的理解


*我只能跟踪
available()
IoBridge.available(fd)
(我没有
IoBridge
)的实现,因此无法完全排除内存泄漏的可能性,尽管可能性极低


**
available()
,如果非阻塞I/O确实是libcore中的内存泄漏,则可以避开该问题。

是否可以提供可疑信息code@QuinnRoundy我已经添加了有问题代码的要点测试和睡眠,并在每个连接上使用一个读取线程。这是一种糟糕的技术。@EJP为什么这是一种糟糕的技术?真的没有这种情况是有利的吗?如果每个对等点都是严格独立的,我会采用传统的方法,但在这种情况下,如果它们在sin上运行,它会大大简化协作gle线程。我花了一段时间来学习新的范例和修改我的许多代码,但我已经让它正常工作,并且内存使用合理。谢谢!
public class Foo implements Runnable{
    private final Selector selector;
    private volatile boolean run;

    public Foo() throws IOException{
        selector = Selector.open();
        run = true;
    }

    public void registerChannel(SocketChannel channel) throws IOException{
        channel.configureBlocking(false);
        // Optionally use a selection key for write as well
        channel.register(selector, SelectionKey.OP_READ);
    }

    public void shutdown(){
        run = false;
        selector.wakeup();
        try{
            selector.close();
        }catch(IOException ignore){}
    }

    public void run(){
        while(run){
            try{
                int readyCount = selector.select();

                // Selector was interrupted or manually woken up
                if(readyCount == 0){
                    // handle appropriately
                }else{
                    Iterator<SelectionKey> iterKeys = selector.selectedKeys().iterator();

                    while(iterKeys.hasNext()){
                        SelectionKey key = iterKeys.next();

                        if(key.isReadable()){
                            SocketChannel chn = (SocketChannel) key.channel();

                            // Process input here
                        }else{
                            // We aren't interested in non-readable channels atm
                        }

                        // Very important
                        iterKeys.remove();
                    }
                }
            }catch(IOException ex) {
                // handle selector exception
            }
        }
    }
}