Hello Mark, Please let me disagree with you. I have connected JVisualVM to Tomcat JVM, run the test with 10,000 connections, performed several explicit GCs and made a heap dump. All 10,000 WsSessions are in heap. They are referenced as following: this <- wsSession - class: org.apache.tomcat.websocket.server.WsHttpUpgradeHandler <- internalHttpUpgradeHandler - class: org.apache.coyote.http11.upgrade.UpgradeProcessorInternal <- val - class: java.util.concurrent.ConcurrentHashMap$Node <- [12930] - class: java.util.concurrent.ConcurrentHashMap$Node[] <- table - class: java.util.concurrent.ConcurrentHashMap <- connections - class: org.apache.coyote.AbstractProtocol$ConnectionHandler <- handler - class: org.apache.tomcat.util.net.NioEndpoint <- this$0 (Java frame) - class: org.apache.tomcat.util.net.NioEndpoint$Acceptor
Do you think it might be Tomcat configuration dependent? Please see my server.xml below. <?xml version='1.0' encoding='utf-8'?> <Server port="8005" shutdown="SHUTDOWN"> <Listener className="org.apache.catalina.startup.VersionLoggerListener" /> <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" /> <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" /> <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" /> <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" /> <GlobalNamingResources> <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" /> </GlobalNamingResources> <Service name="Catalina"> <Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" asyncTimeout="20000" URIEncoding="utf-8" acceptorThreadCount="2" compressableMimeType="text/html,text/xml,text/plain,text/x-json,application/javascript,application/json,text/css" compression="on" connectionTimeout="60000" maxThreads="1024" processorCache="512" redirectPort="8443" /> <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> <Engine name="Catalina" defaultHost="localhost"> <Realm className="org.apache.catalina.realm.LockOutRealm"> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> </Realm> <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log." suffix=".txt" pattern="%h %l %u %t "%r" "%{User-Agent}i" %s %b %Dms" resolveHosts="false"/> </Host> </Engine> </Service> </Server> Thank you, Kirill On Tue, 30 Jul 2019 at 21:45, Mark Thomas <ma...@apache.org> wrote: > On 30/07/2019 05:48, Kirill Ilyukhin wrote: > > Hello Mark, > > > > Please see the test case and Tomcat JVM heap dump screenshot attached. > > For sake of simplicity I do Thread.sleep() in client code instead of > > reading bytes from server. > > That test case does not demonstrate a memory leak in either Tomcat 9.0.x > nor Tomcat 8.5.x. > > There are some WsSession instances in memory at the end of the test but > they are unreachable - i.e. eligible for GC. > > Mark > > > > Test configuration is the following: > > > > Server version: Apache Tomcat/8.5.3 > > Server built: Jun 9 2016 11:16:29 UTC > > Server number: 8.5.3.0 > > OS Name: Mac OS X > > OS Version: 10.14.5 > > Architecture: x86_64 > > JVM Version: 1.8.0_112-b16 > > JVM Vendor: Oracle Corporation > > The APR based Apache Tomcat Native library which allows optimal > > performance in production environments was not found on the > > java.library.path: ... > > > > Thank you, > > Kirill > > > > On Tue, 30 Jul 2019 at 02:15, Mark Thomas <ma...@apache.org > > <mailto:ma...@apache.org>> wrote: > > > > On 26/07/2019 10:33, Kirill Ilyukhin wrote: > > > Hello, > > > > > > When Tomcat receives WebSocket text message with invalid UTF-8, it > > closes > > > this connection with NOT_CONSISTENT reason. But after that some > > objects > > > (WsSession, UpgradeHandler, etc) stay in heap forever. They are > > referenced > > > from AbstractProtocol's connections map. > > > > > > This leak consistently happens with Tomcat 8.5.3 and 8.5.43, both > > on Mac OS > > > and Windows, with or without Tomcat native. > > > > > > I have created a very simple WebSocket Endpoint which does nothing > > except > > > logging its events and incoming messages, please see the code > > below. Also > > > you will need a WebSocket client which sends broken UTF-8 in text > > message > > > right after connecting to the server. > > > > I can't repeat this with either 9.0.x nor 8.5.x. I've repeated the > steps > > described above and checked the resulting state with a profiler. No > > references are retained to WsSession objects nor WsHttpUpgradeHandler > > objects. > > > > You'll need to provide the simplest possible test case (single class > > client, simplest possible WAR) that demonstrates the issue. > > > > Mark > > > > > > > > > > Thank you, > > > Kirill > > > > > > ----------------------- > > > package com.example.wstest; > > > > > > import org.apache.log4j.Logger; > > > import javax.websocket.*; > > > > > > public class WSEndpoint extends Endpoint { > > > private static final Logger logger = > > Logger.getLogger(WSEndpoint.class); > > > private WSConnection connection; > > > > > > @Override > > > public void onOpen(Session session, EndpointConfig config) { > > > connection = new WSConnection(session); > > > logger.info <http://logger.info>("Opened WebSocket > > session-" + session.getId()); > > > } > > > > > > @Override > > > public void onClose(Session session, CloseReason closeReason) { > > > logger.info <http://logger.info>("Closed WebSocket > > session-" + session.getId() + ", > > > reason: " + closeReason.getCloseCode() + " (" + > > > closeReason.getReasonPhrase() + ")"); > > > connection.destroy(); > > > connection = null; > > > } > > > > > > @Override > > > public void onError(Session session, Throwable throwable) { > > > logger.info <http://logger.info>("Error on WebSocket > > session-" + session.getId(), > > > throwable); > > > connection.destroy(); > > > connection = null; > > > } > > > > > > static class WSConnection implements > > MessageHandler.Whole<String> { > > > private final Session session; > > > > > > WSConnection(Session session) { > > > this.session = session; > > > session.addMessageHandler(this); > > > } > > > > > > public void destroy() { > > > session.removeMessageHandler(this); > > > } > > > > > > @Override > > > public void onMessage(String message) { > > > logger.info <http://logger.info>("Session-" + > > session.getId() + " onMessage(" + > > > message +")"); > > > } > > > } > > > } > > > ----------------------- > > > > > > > > > --------------------------------------------------------------------- > > To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org > > <mailto:users-unsubscr...@tomcat.apache.org> > > For additional commands, e-mail: users-h...@tomcat.apache.org > > <mailto:users-h...@tomcat.apache.org> > > > > > > > > --------------------------------------------------------------------- > > To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org > > For additional commands, e-mail: users-h...@tomcat.apache.org > > > > > --------------------------------------------------------------------- > To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org > For additional commands, e-mail: users-h...@tomcat.apache.org > >