我有一个基于javax的WebSocket服务器,它可以在两个使用tomcat 9的客户端之间进行通信。应用程序运行良好7- 8个月,没有。的客户端开始增加,WebSocket调用开始增加,我遇到了java.lang.OutOfMemoryError: Java heap space
错误。
java.lang.OutOfMemoryError: Java heap space
at java.nio.HeapCharBuffer.<init>(HeapCharBuffer.java:57)
at java.nio.CharBuffer.allocate(CharBuffer.java:335)
at org.apache.tomcat.websocket.WsFrameBase.processInitialHeader(WsFrameBase.java:196)
at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:118)
at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:82)
at org.apache.tomcat.websocket.server.WsFrameServer.doOnDataAvailable(WsFrameServer.java:171)
at org.apache.tomcat.websocket.server.WsFrameServer.notifyDataAvailable(WsFrameServer.java:151)
at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.upgradeDispatch(WsHttpUpgradeHandler.java:148)
at org.apache.coyote.http11.upgrade.UpgradeProcessorInternal.dispatch(UpgradeProcessorInternal.java:54)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:53)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:754)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1385)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
字符串
在这里的错误,它说的东西与CharBuffer分配,所以我认为它必须与字符串和分析更多关于它,我采取了我的应用程序的堆转储,下面是图像从JProfiler。
x1c 0d1x的数据
在这里,char[]大约是6 Gb,包含所有常见的onMessage(String message)
调用。
下面是我的项目中的简化代码
public class WsServer {
Logger logger = LogManager.getLogger(WsServer.class);
static CopyOnWriteArrayList<Session> GlobalSessions = new CopyOnWriteArrayList<Session>();
@OnOpen
public void onOpen(Session session, EndpointConfig endpointConfig) {
GlobalSessions.add(session);
}
@OnClose
public void onClose(Session session, CloseReason reason) {
try {
GlobalSessions.remove(session);
} catch (Exception e) {
logger.error(e.getMessage(),e);
}
}
@OnError
public void onError(Session session, Throwable t) {
try {
session.close(new CloseReason(CloseReason.CloseCodes.VIOLATED_POLICY, t.getMessage()));
if(t instanceof IOException) {
logger.error(t.getMessage());
}else {
logger.error(t.getMessage(), t);
}
}catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
@OnMessage(maxMessageSize = 5242880)
public void onMessage(String message, Session session) {
updateLoggerFile(defaultLoggerName);
Logger logger = LogManager.getLogger(WsServer.class);
try {
ObjectMapper mapper = new ObjectMapper();
Message messageObj = mapper.readValue(message, Message.class);
logger.info("message=====>"+messageObj.toString());
broadcastToUISessions(messageObj);
} catch (Exception e) {
logger.error(e.getMessage(),e);
}
}
public void broadcastToSessions(Message messageObj) {
GlobalSessions.forEach(eachSession -> {
try {
if (eachSession.isOpen()) {
synchronized(eachSession) {
eachSession.getBasicRemote().sendText(mapper.writeValueAsString(messageObj));
}
}
} catch (JsonProcessingException e) {
logger.error(e.getMessage(),e);
} catch (IOException e) {
logger.error(e.getMessage(),e);
}
});
}
@OnMessage
public void onMessage(PongMessage pongFrame, Session session) throws SchedulerException {
//simple pingpong mechanism to check the client status
}
static void updateLoggerFile(String filename) {
System.setProperty("abcFileName", ""+filename+".log");
}
}
型
从代码中可以看到,我没有将消息String存储在任何全局变量中,每次新消息到来时,前一条消息都会被延迟,并且应该被垃圾收集。但是这些消息仍然作为软引用保留在GC中。
我错过了什么吗?每次GC运行时,String值不应该定期从堆中清除吗?我是否应该每隔一小时(或在特定时间间隔后)手动调用System.GC()
?我应该为GC设置一些JAVA_OPTS吗?
我发现的一个临时解决方案是将onMessage()
的maxMessageSize从5 Mb减少到1 Mb,并将内存占用减少到1/5。
我将JVM最大堆大小设置为-Xmx8192m
1条答案
按热度按时间f8rj6qna1#
public int findDuplicate = new Duplicate();
这是一个代码的味道给我。静态初始化的示例大多数情况下会造成内存泄漏。检查最佳实践以避免内存泄漏。