我代码中的某个无限循环

时间:2011-08-31 05:06:46

标签: java tcp profiling

我有这个Java游戏服务器,可处理多达3,000个tcp连接,每个播放器或每个tcp连接都有自己的线程,每个线程都是这样的:

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

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

        kickPlayer();

    }catch(IOException e)
    {
        kickPlayer();
    }catch(Exception e)
    {
        kickPlayer();
    }
    finally
    {
        try{
            kickPlayer();
        }catch(Exception e){};

        MainServer.removeIP(ip);
    }
}

代码运行正常,我知道每个玩家的每个线程都是个坏主意,但我现在也会这样做。服务器在快速机器上运行良好(6cor x2,64bits,24GB RAM,Windows Server 2003)。

但是在某些时候,在UpTime大约12个小时之后,服务器开始在某个地方循环...我知道因为java进程无限地占用了99%的CPU,直到下次重启。 而且我很难分析应用程序,因为我不想打扰玩家。我使用的探查器(visualvm)总是最终破坏服务器而不告诉我问题出在哪里。

无论如何,在上面的那段代码中我想可能问题来自于:

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

_in是客户端套接字的BufferedReader

_in.read()是否有可能无限地返回其他内容以保持我的代码运行并占用99%的资源?我的代码有问题吗?我不明白一切,我只写了一半。

3 个答案:

答案 0 :(得分:2)

一次读取一个char几乎与使用+ =构建String一样慢。我无法告诉你哪个更糟糕。如果单个连接使用这种方法绑定整个核心,我不会感到惊讶。

最简单的“修复”是使用BufferedReader和StringBuilder。

然而,读取数据的最有效方法是将字节读入ByteBuffer并解析“行”。我假设您正在接收ASCII文本。您可以编写解析器,以便能够在一个阶段处理内容和行尾(即一次传递数据)

使用最后一种方法,这里是我从套接字解析XML消息并以XML回复的示例(包括代码)。典型的延迟为16微秒,吞吐量为每秒264K。

http://vanillajava.blogspot.com/2011/07/send-xml-over-socket-fast.html


您可以执行以下可能足够快的操作

BufferedReader br = new BufferedReader(_in);
for(String line; ((line = br.readline()) != null;) {
    if(line.indexOf('\0') >= 0)
       for(String part: line.split("\0"))
          parsePlayerPacket(part);
    else
       parsePlayerPacket(line);
}

如果你发现这个解决方案很简单并且熟悉ByteBuffer,你可以考虑使用它们。

答案 1 :(得分:0)

我在我写的一个应用程序中遇到了同样的问题。我的应用程序占用了50%的CPU(双核)。

我所做的是解决问题,让线程睡1时间点

Thread.sleep(1);

我希望这对你有所帮助

编辑:

哦,那是什么?
}catch(IOException e)
{
kickPlayer();
}catch(Exception e)
{
kickPlayer();
}

我认为你不需要IOException Catch(异常捕获,捕获各种异常)

答案 2 :(得分:0)

那个异常处理只是伤害了我的眼睛。因为你最后再次调用它,所以在catch块中调用kickPlayer()是没有意义的。最后执行(几乎)总是。

现在关于你的问题,忘了我之前的回答,我有点睡着了XD。在发布的while循环中,我没有看到任何容易循环的东西。当没有更多数据或抛出异常时,InputStream.read()返回-1。问题必须在其他代码中,或者可能是线程问题。

正如他们在其他答案中告诉你的那样,尝试使用缓冲流,一次只读一个字符块而不是一个,并替换StringBuilder的append方法的字符串连接。这应该可以提高性能,但不确定它是否能解决问题(可能会在24小时而不是12小时内出现)。