jvm 内存不足:Java堆空间

qjp7pelc  于 10个月前  发布在  Java
关注(0)|答案(1)|浏览(80)

我有一个基于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

f8rj6qna

f8rj6qna1#

public int findDuplicate = new Duplicate();
这是一个代码的味道给我。静态初始化的示例大多数情况下会造成内存泄漏。检查最佳实践以避免内存泄漏。

相关问题