BufferedReader.read()吃了100%的CPU

问题描述:

我有一个JAVA游戏服务器,每个TCP连接使用1个线程。 (我知道这很糟糕,但现在我必须保持这种状态)。上的(3.2Ghz的6cor X2机,24GB RAM,Windows Server 2003和的64位),并在这里是一块代码:BufferedReader.read()吃了100%的CPU

public void run() 
{ 
    try 
    { 
     String packet = ""; 
     char charCur[] = new char[1]; 

     while(_in.read(charCur, 0, 1)!=-1 && Server.isRunning) 
     { 
      if (charCur[0] != '\u0000' && charCur[0] != '\n' && charCur[0] != '\r') 
      { 
       packet += charCur[0]; 
      }else if(!packet.isEmpty()) 
      { 
       parsePlayerPacket(packet); 
       packet = ""; 
      } 
     } 

    }catch(Exception e) 
    { 
     e.printStackTrace(); 
    } 
    finally 
    { 
     try{ 
      kickPlayer(); 
     }catch(Exception e){e.printStackTrace();}; 

     Server.removeIp(_ip); 
    } 
} 

服务器运行时间约12小时或更长时间(约3.000玩家连接)后,将服务器开始吃掉所有12个CPU的100%,直到我手动重新启动JAVA应用程序。所以比赛开始落后严重,我的球员开始抱怨。

我试图分析应用程序和这里是我想出了:

Screenshot of my profiling result

所以我猜这个问题是从这里来的:

while(_in.read(charCur, 0, 1)!=-1 && Server.isRunning) 

明知变量“_in”是套接字输入的读取器:(_in = new BufferedReader(new InputStreamReader(_socket.getInputStream())))。

为什么在长时间的服务器uptime之后,_in.read()需要很多CPU?

我试过把Thread.sleep(1);和更多内部的循环,但没有做任何事情,我猜这个问题是在BufferedReader.read()方法中。

有没有人有什么可以导致这种情况?以及如何解决它?

+6

我很惊讶它的这个,而不是事实,你使用字符串连接在一个循环。 *为什么*你一次只能读一个字符? –

+0

数据包非常小,例如:“AB123”。所以没关系。 – Reacen

+2

直到你最终被发送一个巨大的字符串被某人发动DDOS攻击。读取多个字符和*还可以使用StringBuilder非常简单...为什么不这样做呢? –

我不知道为什么这个调用很慢,但我永远不会在严格的循环中一次读取一个字节。谁知道内部函数有什么样的开销。

我会读取当前流中可用的所有数据并解析该数据。 这需要一个缓冲区和一些额外的簿记,但无论如何都比从流中逐字节读取更快。

+0

你能否提供一个代码示例? – Reacen

+0

Java BufferedReader被缓冲(nomen est omen ...)并且每次从一个字节读取一个字节比较常见。当直接从FileInputStream中读取并将其包装到BufferedInputStream中时,您可以观察性能差异。 –

'1个每个TCP连接线程' '约3.000玩家' 连接

= 3.000线程?

我的猜测:一次可以重复复制一个字节的线程的最大数量约为3.000。这听起来不太奇怪。

解决方案:减少线程数量,一次读取更多字节。

您可以使用executorService。在javadoc中有一个简单的例子:http://download.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html

+0

请问我可以把我和一些关于线程池的简单话题联系起来,或者有什么可以解决这个线程问题? – Reacen

它看起来并不像你曾经关闭过BufferedReader,除非你正在kickPlayer()方法中尝试它。

每个读者可能比你意识到的生活时间长很多。

+0

如何关闭它? – Reacen

+0

在while循环之后或在finally块中调用_in.close()。 – FacilityDerek

+0

捕获并处理IOExceptions。 – FacilityDerek

这是上一个问题的复本:An infinite loop somewhere in my code。请不要打开一个新的问题,而是使用编辑功能。

话虽这么说,3000线,绝对是很多,很可能会造成的上下文切换过量。不要为每个连接启动一个新线程,而应考虑在Java中使用非阻塞IO设施。例如可以在这里找到:http://download.oracle.com/javase/1.4.2/docs/guide/nio/example/index.html

我相信我是来面临着同样的问题,我猜它是Java中的错误。我在BufferedReader.read() eating 100% of CPU发布了我的问题 - 也许有人会在那里给出答案,这也会对你有所帮助。