BufferedReader.read (), потребляющий 100% ресурсов процессора - PullRequest
9 голосов
/ 02 сентября 2011

У меня есть игровой сервер JAVA, который использует 1 поток на каждое соединение TCP.(Я знаю, что это плохо, но мне придется сейчас так держать).На компьютере (3,2 ГГц 6cor x2, 24 ГБ ОЗУ, Windows Server 2003 64бит) и вот фрагмент кода:

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 или более часов работы сервера upTime (и около 3000 подключенных игроков) сервер начинает пожирать 100% всех 12 процессоров навсегда, пока я не перезагружу приложение JAVA вручную.Так что игра начинает очень сильно отставать, и мои игроки начинают жаловаться.

Я попытался профилировать приложение, и вот что я придумал:

Screenshot of my profiling result

ИтакЯ предполагаю, что проблема возникает отсюда:

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

зная, что переменная "_in" является читателем ввода сокета: (_in = new BufferedReader (new InputStreamReader (_socket.getInputStream ()))).

С какой стати _in.read () так долго загружает ЦП после долгого времени работы сервера?

Я пытался поставить Thread.sleep (1);и многое другое внутри цикла while, но ничего не делает, я думаю, проблема в методе BufferedReader.read ().

Кто-нибудь имеет представление о том, что может вызвать это ??И как это исправить?

Ответы [ 4 ]

3 голосов
/ 02 сентября 2011

Это дубликат вашего предыдущего вопроса: Бесконечный цикл где-то в моем коде . Пожалуйста, не открывайте новый вопрос, а используйте функции редактирования.

При этом 3000 потоков - это определенно много и, скорее всего, вызовут чрезмерное переключение контекста. Вместо того, чтобы начинать новый поток для каждого соединения, рассмотрите возможность использования неблокирующих средств ввода-вывода в Java. Примеры можно найти здесь: http://download.oracle.com/javase/1.4.2/docs/guide/nio/example/index.html

1 голос
/ 02 сентября 2011

Я не знаю, почему вызов медленный, но я бы никогда не читал по одному байту за раз в тесном цикле. Кто знает, какие накладные расходы имеет внутренняя функция.

Я бы прочитал все данные, доступные в данный момент в потоке, и проанализировал их. Это потребует буфера и некоторой дополнительной бухгалтерии, но в любом случае быстрее, чем чтение байта из потока.

0 голосов
/ 02 сентября 2011

Не похоже, что вы когда-либо закрываете BufferedReader, если только вы не пытаетесь сделать это с помощью метода kickPlayer ().

Каждый читатель может жить намного дольше, чем вы думаете.

0 голосов
/ 02 сентября 2011

'1 поток на TCP-соединение' 'около 3000 подключенных игроков'

= 3000 потоков?!

Мое предположение: максимальное количество потоков, которое может повторятьсякопирование одного байта за один раз составляет около 3000.Это не звучит так странно.

Решение: меньше потоков и больше байтов за один раз.

Вы можете использовать executorService.В javadoc есть упрощенный пример: http://download.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html

...