Java服务器程序耗尽堆,烧掉CPU
我正在制作一个小的网络游戏来学习Java中的网络,我需要了解一下我的程序有什么问题。我的服务器程序将堆最大化,并消耗100%的CPU。我确信我的代码中有一些主要的新手陷阱,我想知道是否有人愿意向我指出它们,也许还可以详细说明为什么这是一个如此可怕的做法 基本上,服务器类的工作是在socket.accept()中等待以处理新客户机。那里的每个客户机都有自己的ConnectionRead(处理输入)和附加的OutputStream(处理输出)。我知道这对于大型应用程序来说可能是浪费,但由于服务器仅与三个客户端一起运行(设置为跳过渲染,并且仅每隔20毫秒通过套接字发送输入和输出数据),这会烧坏CPU,服务器会溢出堆栈 我有一个Packet类,它将数据转换成字符串进行发送,然后接收器将其解码回一个包。我怀疑我有一些包裹放得太久了,但我不知道放在哪里。如果不是数据包,我相当确定我有某种不受控制的指数对象增长 下面是一些相关代码的片段。如果问题在别处,我很乐意提供更多 以下是完整代码,仅供参考: 服务器:Java服务器程序耗尽堆,烧掉CPU,java,Java,我正在制作一个小的网络游戏来学习Java中的网络,我需要了解一下我的程序有什么问题。我的服务器程序将堆最大化,并消耗100%的CPU。我确信我的代码中有一些主要的新手陷阱,我想知道是否有人愿意向我指出它们,也许还可以详细说明为什么这是一个如此可怕的做法 基本上,服务器类的工作是在socket.accept()中等待以处理新客户机。那里的每个客户机都有自己的ConnectionRead(处理输入)和附加的OutputStream(处理输出)。我知道这对于大型应用程序来说可能是浪费,但由于服务器仅与
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的易变性很好。我错过了那个。谢谢