什麼,你遇到的是不是內存泄漏,而是垃圾收集器無法以跟上你每秒創建的大量垃圾對象*最終,你會發現你的應用程序暫時大量凍結,因爲gc調用停止世界的技術來批量刪除不需要的對象。
從您的代碼中,我可以看到您正在嘗試使用單個線程輪詢來自多個流的可用性。我認爲你在這裏創建的是Java/Android非阻塞I/O的一個非常簡單和低效的實現。
鑑於堆棧跟蹤,我也假設你在這種情況下使用套接字。使用ServerSocketChannel
和SocketChannel
以及Selector
可以爲您提供所需的功能。 選擇器和SocketChannel只有的示例實現在下面提供(如果這是服務器端代碼,則應該使用相同邏輯實現ServerSocketChannel
)。
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
}
}
}
}
類Foo
充當可運行來創建一個線程並多路複用SocketChannel
以有效的方式,阻斷(相對於快速輪詢)插座,直到一個是可用的。這不僅消除了大量對象創建的問題,還消除了在不同平臺上可能不可靠的available()
**的使用。這種設計模式還帶來了額外的好處,因爲它不必按順序遍歷套接字列表,因此可能比使用大量套接字的方法更快。
查看關於Selector和SocketChannel以及alternative example的教程以獲得更深入的瞭解。
*我只能追溯至available()
的IoBridge.available(fd)
執行(我有沒有IoBridge
來源),因此內存泄漏不能完全排除,雖然這是極不可能的。
** available()
的實現差別很大,不應該用作有多少數據可用的絕對指標。從我發現的情況來看,java的PlainSocketImpl
硬編碼對套接字返回0。即使android通過IoBridge.available(fd)
有一個有效的available()
,如果事實證明確實是libcore中的內存泄漏,您也可以避開非阻塞I/O的問題。
你能否提供可疑代碼 – ydobonebi
@QuinnRoundy我已經添加了有問題代碼的要點。 – David
擺脫'available()'測試和睡眠併爲每個連接使用一個讀取線程。這是不好的技術。 – EJP