Java服务器程序耗尽堆,烧掉CPU

Java服务器程序耗尽堆,烧掉CPU,java,Java,我正在制作一个小的网络游戏来学习Java中的网络,我需要了解一下我的程序有什么问题。我的服务器程序将堆最大化,并消耗100%的CPU。我确信我的代码中有一些主要的新手陷阱,我想知道是否有人愿意向我指出它们,也许还可以详细说明为什么这是一个如此可怕的做法 基本上,服务器类的工作是在socket.accept()中等待以处理新客户机。那里的每个客户机都有自己的ConnectionRead(处理输入)和附加的OutputStream(处理输出)。我知道这对于大型应用程序来说可能是浪费,但由于服务器仅与

我正在制作一个小的网络游戏来学习Java中的网络,我需要了解一下我的程序有什么问题。我的服务器程序将堆最大化,并消耗100%的CPU。我确信我的代码中有一些主要的新手陷阱,我想知道是否有人愿意向我指出它们,也许还可以详细说明为什么这是一个如此可怕的做法

基本上,服务器类的工作是在socket.accept()中等待以处理新客户机。那里的每个客户机都有自己的ConnectionRead(处理输入)和附加的OutputStream(处理输出)。我知道这对于大型应用程序来说可能是浪费,但由于服务器仅与三个客户端一起运行(设置为跳过渲染,并且仅每隔20毫秒通过套接字发送输入和输出数据),这会烧坏CPU,服务器会溢出堆栈

我有一个Packet类,它将数据转换成字符串进行发送,然后接收器将其解码回一个包。我怀疑我有一些包裹放得太久了,但我不知道放在哪里。如果不是数据包,我相当确定我有某种不受控制的指数对象增长

下面是一些相关代码的片段。如果问题在别处,我很乐意提供更多

以下是完整代码,仅供参考:

服务器:

public Server() {
    network = new NetworkManager(this);
    network.setConnectionCounter(0);
    entityManager = new EntityManager(this);
    setListenState(true);

    try {
        serverSocket = new ServerSocket(PORT);

    } catch (IOException e) {
        System.out.println("Error in server constructor.");
        System.exit(1);
    }
}

public void listen() {

        System.out.println("Current connectionCounter: " + network.getConnectionCounter()); 
        while (shouldListen) {
            ConnectionThread conn = null;

            try {
                conn = new ConnectionThread(serverSocket, this);
            }
            catch (Exception e) {
                System.out.println("____Error constructing ConnectionThread. Could there be another instance of the server running?");
                e.printStackTrace();
                System.exit(1);
            }

            (new Thread(conn)).start();


            System.out.println("Connection count: " + network.getConnectionCounter());
        }
    }
连接如下:

public ConnectionThread(ServerSocket s, Server ser) {
    resetTimer();
    setActiveState(false);
    server = ser;

    //This UUID becomes the client's controllable player ID
    //and the ID of this ConnectionThread.
    connectionID = String.valueOf(UUID.randomUUID());

    try {
        socket = s.accept();
        System.out.println("Socket ID " + connectionID + " established on: " + socket);
    } catch (IOException e) {
        System.out.println("Error in ConnectionThread. Is there a server already running on this port?");
    }
    init();
}

public void init() {
        try {
            out = new PrintWriter(socket.getOutputStream(), true);
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

        }
        catch (IOException e) {
            System.out.println("Error in intializing I/O streams.");
            System.exit(1);
        }

        //Create the output thread
        OutputStream outputHandler = new OutputStream(this);
        new Thread(outputHandler).start();


        //Get the client up to date on all relevant data
        server.getEntityManager().addPlayerEntity(getConnectionID());
        server.getNetwork().pushClientInitState(getConnectionID(), this);
        server.getNetwork().addConnection(this);
        server.getNetwork().notifyClientsAboutNewPlayer(getConnectionID());
        int s = server.getNetwork().getConnections().size();
        server.getNetwork().sendConsoleMessage("Players online: " + s, this);

    }

public void run() {
    setActiveState(true);
    System.out.println("Running ConnectionThread...");
    while (isActive()) {


        //System.out.println("Entity size: " + server.getEntityManager().getEntities().size());
            String op = readInputStream();

            if (op.equals("")) continue;
            Packet packet = Packet.populateNewPacketFromString(op);
            try {
                incomingOpQueue.put(packet);
            } catch (InterruptedException e) {
                System.out.println("Server failed to add packet to outgoing queue!");
            }
            //Take all packets off the incoming queue and execute them in order
            while (incomingOpQueue.size() > 0) {
                Packet p = incomingOpQueue.poll();
                PacketExecutor.executePacket(server, p, this);


            }
    }
}

public String readInputStream() {
    String msg = "";

    try {
        msg = in.readLine();
        msg = msg.replace("\n", "");
        msg = msg.trim();
    } catch (IOException e) {
        return "";
    }
    return msg;

}
输出流:

public void output() {
    while (parentCT.isActive()) {
        UnitTester.updateAllEntityLocationsToAllClients(parentCT, parentCT.server.getEntityManager().getEntities());
        while (parentCT.getOutgoingOpQueue().size() > 0) {
            String packet = (parentCT.getOutgoingOpQueue().poll().getString());
            if (packet.equals("")) continue;
            //System.out.println("Sending " + packet + " to " + parentCT.getConnectionID());
            parentCT.getOutput().println(packet);

        }

    }
}

您可能需要将
shouldListen
设置为volatile。否则,它的值很可能会被缓存,而在其他线程中将其设置为false也不会有什么区别。您将其设置为false right,这样主循环就不会产生很多线程,直到它将堆最大化并烧掉CPU?

不,这是不可能的,您不能在java中泄漏内存/fanboyismA 100%的CPU表示某个地方发生了一个大的自旋锁。可能是output()方法。我会粘一根线。睡觉(10);在那里,看看它是否能让CPU平静一点。在那里放一个睡眠(10)实际上对CPU有很大的影响。从90-100%下降到50%。就堆而言,这也产生了巨大的差异,但几分钟后,堆大小开始线性累积。我不知道内存泄漏可能在哪里,但我会继续查找。使用jvisualvm找出问题所在。这就是我目前正在使用的,尽管我刚刚开始学习分析的艺术!我在他的代码片段中没有看到任何东西将
shoulldlisten
设置为false。无论如何,它都会阻塞serverSocket.accept(),所以这是一个限制因素。正如Kayaman所说,ConnectionRead构造函数中有一个serverSocket.accept()方法,它会阻止新线程的创建,直到它可以与客户端配对。但我刚刚检查了——shouldListen的易变性很好。我错过了那个。谢谢