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应用程序。所以比赛开始落后严重,我的球员开始抱怨。
我试图分析应用程序和这里是我想出了:
所以我猜这个问题是从这里来的:
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()方法中。
有没有人有什么可以导致这种情况?以及如何解决它?
我不知道为什么这个调用很慢,但我永远不会在严格的循环中一次读取一个字节。谁知道内部函数有什么样的开销。
我会读取当前流中可用的所有数据并解析该数据。 这需要一个缓冲区和一些额外的簿记,但无论如何都比从流中逐字节读取更快。
你能否提供一个代码示例? – Reacen
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
请问我可以把我和一些关于线程池的简单话题联系起来,或者有什么可以解决这个线程问题? – Reacen
它看起来并不像你曾经关闭过BufferedReader,除非你正在kickPlayer()方法中尝试它。
每个读者可能比你意识到的生活时间长很多。
如何关闭它? – Reacen
在while循环之后或在finally块中调用_in.close()。 – FacilityDerek
捕获并处理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发布了我的问题 - 也许有人会在那里给出答案,这也会对你有所帮助。
我很惊讶它的这个,而不是事实,你使用字符串连接在一个循环。 *为什么*你一次只能读一个字符? –
数据包非常小,例如:“AB123”。所以没关系。 – Reacen
直到你最终被发送一个巨大的字符串被某人发动DDOS攻击。读取多个字符和*还可以使用StringBuilder非常简单...为什么不这样做呢? –