Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory leak on Tomcat when websocket endpoint terminated #332

Closed
billyyarosh opened this issue May 2, 2012 · 8 comments
Closed

Memory leak on Tomcat when websocket endpoint terminated #332

billyyarosh opened this issue May 2, 2012 · 8 comments

Comments

@billyyarosh
Copy link

Threads continue to be created on Tomcat every time the endpoint connection is lost "due to a protocol error". The Async-Write and BroadcasterConfig threads keep duplicated when the Atmosphere resource is re-sent to the server. I am using Spring MVC controller to subscribe a client to the BroadcastFactory. Each re-connect subscribe is called, but the client is already subscribed, so you wouldn't expect these threads to keep duplicating.

@billyyarosh
Copy link
Author

Nevermind on this issue. I have a call to:
atmoResource.setBroadcaster(broadcasterFactory.get());
And I do this before checking if the client is subscribed. Sorry about that.

@jfarcand
Copy link
Member

jfarcand commented May 2, 2012

Ok I need more information about that. Can you paste the Thread dump here? Also, are you destroying Broadcaster (because every new Broadcaster will create a new BroadcasterConfig => 2 executors)

Did you know that you can share the Executor between Broadcaster?

org.atmosphere.cpr.broadcaster.shareableThreadPool [default = "false"]

@billyyarosh
Copy link
Author

This does bring up a separate question for me. Is there a way for multiple clients to share the same broadcaster, while receiving different dispatches?

My current code for adding a subscription:

public boolean subscribe(String userName, String realm, FeedToken.FeedType feedType, Object resource)
            throws InvalidTargetObjectTypeException{
        if(!(resource instanceof AtmosphereResource)){
            throw new InvalidTargetObjectTypeException("JSON Feed Dispatcher expects subscription resource" +
                    " of type AtmosphereResource");
        }
        boolean subscribed = this.isSubscribed(userName, realm, feedType);
        //make sure they are not already subscribed before creating a new broadcaster.
        if(!subscribed){
            AtmosphereResource atmoResource = (AtmosphereResource)resource;
            atmoResource.setBroadcaster(broadcasterFactory.get());//recreate a new broadcaster for this resource
            broadcasterFactory.add(atmoResource.getBroadcaster(),
                    new SubscriptionKey(userName, realm, feedType));
            AtmosphereUtils.suspend(atmoResource);
        }
        return subscribed;
    }

And then for dispatching Feeds:

public Future<String> dispatch(FeedToken feed, DispatchType dispatchType){
        Future<String> future = null;
        final ObjectMapper mapper = new ObjectMapper();

        try{
            SubscriptionKey subscriptionKey = new SubscriptionKey(feed.getUserId(), feed.getRealmId(), feed.getType());
            Broadcaster broadcaster = this.getBroadcaster(subscriptionKey);
            if(broadcaster !=null){
                final Map<String, Object> objectMap = new HashMap<String, Object>();
                objectMap.put("dispatchType", dispatchType);
                objectMap.put("feeds", feed);
                future = broadcaster.broadcast(mapper.writeValueAsString(objectMap));
            }
        }
        catch (IOException ioErr){
            logger.log(Level.SEVERE, "Unable to serialize " + feed.getClass().getName() + " as String");
            logger.log(Level.SEVERE, feed.toString());
        }

        return future;
    }

@jfarcand
Copy link
Member

jfarcand commented May 2, 2012

Yes, you can share the same Broadcaster and filter broadcast using BroadcastFilter or PerRequestBroadcastFilter.

Also have you configured a BroadcasterFactoryLifecyclePolicy I don't think there is a leak here but if you can share a thread dump I will be sure

@billyyarosh
Copy link
Author

There is not. After updating with the above code, there is no leak. Thanks, I would look into the Filters.
This is a thread dump after my fix. Before, this would have shown 2 Atmosphere-BroadcasterConfig-0 and Writers for each attempt to reconnect.

"ActiveMQ Transport: tcp://devaimpt01.dev.riskchecknow.com/173.227.221.231:61616"@3,546 in group "main": RUNNING
"ActiveMQ Transport: tcp://devaimpt01.dev.riskchecknow.com/173.227.221.231:61616"@3,583 in group "main": RUNNING
"ActiveMQConnection[ID:wyarosh-mac.office.appriss.com-63586-1335964814996-0:1] Scheduler"@3,526 in group "main": WAIT
"ActiveMQConnection[ID:wyarosh-mac.office.appriss.com-63586-1335964814996-0:2] Scheduler"@3,580 in group "main": WAIT
"ajp-bio-8009-Acceptor-0"@6,329 in group "main": RUNNING
"ajp-bio-8009-AsyncTimeout"@6,331 in group "main": SLEEPING
"Atmosphere-AsyncWrite-0"@7,653 in group "main": WAIT
"Atmosphere-AsyncWrite-0"@5,527 in group "main": WAIT
"Atmosphere-BroadcasterConfig-0"@7,652 in group "main": WAIT
"Atmosphere-BroadcasterConfig-0"@5,525 in group "main": WAIT
"AWT-AppKit"@6,044 in group "main": RUNNING
"ContainerBackgroundProcessor[StandardEngine[Catalina]]"@6,311 in group "main": SLEEPING
"Finalizer"@6,607: WAIT
"GC Daemon"@1,039: WAIT
"http-bio-8445-Acceptor-0"@6,327 in group "main": RUNNING
"http-bio-8445-AsyncTimeout"@6,328 in group "main": SLEEPING
"http-bio-8445-exec-1"@6,333 in group "main": WAIT
"http-bio-8445-exec-10"@7,178 in group "main": WAIT
"http-bio-8445-exec-2"@6,376 in group "main": WAIT
"http-bio-8445-exec-3"@6,541 in group "main": WAIT
"http-bio-8445-exec-4"@7,158 in group "main": WAIT
"http-bio-8445-exec-5"@7,167 in group "main": WAIT
"http-bio-8445-exec-6"@7,168 in group "main": RUNNING
"http-bio-8445-exec-7"@7,172 in group "main": WAIT
"http-bio-8445-exec-8"@7,173 in group "main": WAIT
"http-bio-8445-exec-9"@7,177 in group "main": WAIT
"http-bio-9000-Acceptor-0"@6,321 in group "main": RUNNING
"http-bio-9000-AsyncTimeout"@6,323 in group "main": SLEEPING
"InactivityMonitor ReadCheck"@3,563 in group "main": WAIT
"InactivityMonitor WriteCheck"@3,564 in group "main": WAIT
"main"@1 in group "main": RUNNING
"MongoCleaner1603890109"@2,373 in group "main": SLEEPING
"MongoCleaner1877890613"@4,707 in group "main": SLEEPING
"org.springframework.jms.listener.DefaultMessageListenerContainer#0-1"@3,550 in group "main": WAIT
"org.springframework.jms.listener.DefaultMessageListenerContainer#0-2"@3,551 in group "main": WAIT
"org.springframework.jms.listener.DefaultMessageListenerContainer#0-3"@3,552 in group "main": WAIT
"org.springframework.jms.listener.DefaultMessageListenerContainer#0-4"@3,553 in group "main": WAIT
"org.springframework.jms.listener.DefaultMessageListenerContainer#0-5"@3,554 in group "main": WAIT
"org.springframework.jms.listener.DefaultMessageListenerContainer#1-1"@3,584 in group "main": WAIT
"org.springframework.jms.listener.DefaultMessageListenerContainer#1-2"@3,585 in group "main": WAIT
"org.springframework.jms.listener.DefaultMessageListenerContainer#1-3"@3,586 in group "main": WAIT
"org.springframework.jms.listener.DefaultMessageListenerContainer#1-4"@3,587 in group "main": WAIT
"org.springframework.jms.listener.DefaultMessageListenerContainer#1-5"@3,588 in group "main": WAIT
"Poller SunPKCS11-Darwin"@1,082 in group "main": SLEEPING
"Reference Handler"@7,181: WAIT
"Signal Dispatcher"@7,180: RUNNING

@billyyarosh
Copy link
Author

Ok, I was able to write a filter that handles multiple subscriptions on the same broadcaster. Now I have 2 waiting threads instead of 2 or more per client. Thanks for the advice.

@jfarcand
Copy link
Member

jfarcand commented May 2, 2012

Great news! I've also added official documentation

@billyyarosh
Copy link
Author

Very useful. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants