[英]Serious memory leak when copying stream(simulate telnet) using Java
下面的鏈接是Telnet的Apache commonsnet示例,它使用telnet客戶端(鍵入telnet命令並獲取輸出)模擬用戶,一個線程正在讀取流,一個線程正在寫入流。
http://www.docjar.com/html/api/examples/weatherTelnet.java.html
我修改了該程序,用戶可以登錄telnet服務器並鍵入命令,但從未注銷。 大約每15秒鍾,用戶鍵入一個命令,服務器將其輸出。 我使用另一個線程將服務器輸出復制到本地文件,但是該程序可以運行,但是會導致嚴重的內存泄漏,大約幾個小時后,由於OutofMemoryException,程序退出了,使用Java堆內存轉儲工具,我可以看到根目錄原因是char [] ,它肯定是在復制流期間引起的。
有人可以幫我指出問題出在哪里以及如何解決嗎? 非常感謝。
public static final void readWrite( final InputStream remoteInput, final OutputStream remoteOutput,
final String address ) throws FileNotFoundException, InterruptedException
{
Thread reader = null;
Thread writer = null;
final String[] commands = new String[]{ "username\n", "password\n", "command\n" };
reader = new Thread("reader")
{
@SuppressWarnings( "null" )
@Override
public void run()
{
String currentCommand = null;
try
{
int i = 0;
while( !interrupted() )
{
if( i >= 3 )
currentCommand = "command\n";
else
currentCommand = commands[i++];
remoteOutput.write( currentCommand.getBytes() );
remoteOutput.flush();
sleep( 15000 );
}
}
catch( IOException e )
{
e.printStackTrace();
this.interrupt();
}
catch( InterruptedException e )
{
this.interrupt();
}
}
};
writer = new Thread("writer" )
{
@Override
public void run()
{
try
{
while( !isInterrupted() )
{
FileOutputStream fos = new FileOutputStream( "logfile" );
copyStream( remoteInput, fos, 1024, false );
fos.close();
sleep( 10000 );
}
}
catch( IOException e )
{
this.interrupt();
}
catch( InterruptedException e )
{
this.interrupt();
}
}
};
writer.setPriority( Thread.currentThread().getPriority() + 1 );
}
reader.start();
Thread.sleep( 2000 );
writer.start();
Thread.currentThread().join();
}
public static final long copyStream( InputStream source, OutputStream dest, int bufferSize, boolean flush )
throws CopyStreamException
{
int bytes;
long total;
byte[] buffer;
buffer = new byte[ bufferSize ];
total = 0;
try
{
while( ( bytes = source.read( buffer ) ) != -1 )
{
// Technically, some read(byte[]) methods may return 0 and we cannot
// accept that as an indication of EOF.
if( bytes == 13 )
{
dest.flush();
break;
}
if( bytes == 0 )
{
bytes = source.read();
if( bytes < 0 )
break;
dest.write( bytes );
if( flush )
dest.flush();
++total;
continue;
}
dest.write( buffer, 0, bytes );
if( flush )
dest.flush();
total += bytes;
}
}
catch( IOException e )
{
throw new CopyStreamException( "IOException caught while copying.", total, e );
}
return total;
}
評論范圍太有限,請允許我在此處輸入。
整個應用程序將一直存在,直到最后一個非守護線程退出為止。
是的,我應該在不使用主線程join()的情況下設置線程守護程序之一,對嗎?
您不需要設置優先級,它們通常不會按照您的想法做。
好
中斷要返回/結束的線程沒有意義。
是的,應該刪除此
Interrupt();
廢話
為什么每當精確讀取13個字節時都要刷新數據?
好吧,這是我真正的需求所致,這是一個糟糕的設計,但這將是另一個主題,請參閱下文。
您的0長度數據不會在阻塞連接中發生,但如果確實發生,則您的處理方式不正確,如果沒有它,您會更好。
好吧,實際上此方法是從Apache Commons net復制的,請參見此處:
org. Apache. Commons. Net. Io. Util. Copystream(... )
org. Apache. Commons. Net. Io. Util. Copystream(... )
CopyStream復制數據直到流結束,但是您將其置於循環中,這意味着10秒鍾后您將截斷文件並用無數據替換它(因為流仍處於關閉狀態,並且您未追加日志文件)
是的,這就是我所需要的,我只需要最新的命令輸出,上一個命令對我沒有任何意義,如果我不截斷文件,文件很快就會變得越來越大。
您的“讀取器”寫入數據,但不讀取任何內容。 您的“作家”線程可以讀寫。
是的,也許不是一個好名字,我稍后再更改。
您正在直接使用線程,這被認為是不好的做法。 您應該使用Runnable並用Thread進行包裝,或者使用ExecutorService
是的,我稍后再更改。
您會捕獲許多異常,但不要將它們打印出來。 勇敢地假設您不需要知道什么時候拋出它們,因為您的線程可能會以靜默方式死掉,您也不知道為什么。
是的,我稍后會對其進行修改並添加日志。
我假設CopyStreamException是一個Exception,在這種情況下,您麻煩創建一個包裝IOException的不錯的異常,然后將其丟棄。 在啟動閱讀器和編寫器之間,您無需睡覺。
是的,我想是這樣。
由於您的當前線程僅在等待其他兩個線程,因此可以使用當前線程來編寫“閱讀器”,並且只有一個后台日志記錄線程。
是的,僅適用於獨立應用程序,實際上這段代碼是從Web應用程序(服務器端)中提取的,主線程有另一項工作,希望我制作的兩個線程永不死(當Web服務器運行時繼續運行)
該方法不會引發FileNotFoundException。
它拋出,請參見外部方法聲明。
您計算了復制的總數據,但從未使用過。
是的,應刪除。
要復制流,建議您使用IOUtils。 復制(InputStream,OutputStream)
我以前使用過,但是它不能滿足我的需要:它將一直阻塞,並且日志文件的大小很快變得太大,以至於我不得不使用自定義的文件。
為什么要讀取13個字節並退出?
當然,這是一個神奇的數字。 我發現如果讀取了13個字節,那么它將到達輸出的末尾。 顯然這是一個不好的設計,您能幫我找到一種更不錯的方法嗎?
當前一個線程鍵入命令時,服務器將返還大約300Kb的內容。
我需要一個可以檢測到服務器輸出結束的方法,但是由於
while( ( bytes = source. Read( buffer ) )!= -1 )
剛剛阻塞,我無法弄清楚它的while( ( bytes = source. Read( buffer ) )!= -1 )
!
您的程序沒有char[]
因此,如果遇到OutOfMemoryError錯誤,則創建char []可以向我們顯示代碼行,該代碼行嗎?
我本來要寫評論,但是太長了。
您是否泄漏來自copyStream方法? 您如何確定的? 要回顯@Peter Lawrey的初始語句,代碼中唯一引用char []的位置是創建的實際字符串。 由於您的程序運行時間很長,因此始終創建要處理的字符串,並假設它們是唯一的字符串(即,JVM的字符串池未重用,而是為每個字符串創建了新的字符串),這可以解釋您的內存使用情況。 您可能只是有很多字符串...
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.