Smack create too much threads with newCachedThreadPool

when I need to send a large number of IQ in short time, smack will create large number of thread with CachedThreadPool. And this cause crashes on Huawei phones with Android 7.0 or higher.

I got

"java.lang.OutOfMemoryError

pthread_create (1040KB stack) failed: Out of memory

1 java.lang.Thread.nativeCreate(Native Method)

2 java.lang.Thread.start(Thread.java:745)

3 java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:941)

4 java.util.concurrent.ThreadPoolExecutor.processWorkerExit(ThreadPoolExecutor.jav a:1009)

5 java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1151)

6 java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)

7 java.lang.Thread.run(Thread.java:776)

". When I reappear the crash, I found more then 120 thread create by “Smack-Cached Executor”, and I modify the cachedExecutorService as newFixedThreadPool (in smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java), the APP works well.

I hope this will help.

1 Like

when I need to send a large number of IQ in short time, smack will create large number of thread with CachedThreadPool.
How do you send those IQs? Using sendStanza() or createStanzaCollectorAndSend() should not create extra threads. And Smack’s sendWithResponseCallback() API does not use the “Smack-Cached Executor”.

The “Smack-Cached Executor” is used when invoking stanza listeners asynchronously. Maybe one of those listeners blocks when it shouldn’t? This would cause the thread count of the executor to increase.

We send IQs with “sendStanzaWithResponseCallback” almostly. and in fact it used “Smack-Cached Executor”.

We send IQ asynchronously. It looks like this:

public void sendStanza(final Stanza stanza, final IRequestListener listener) {
    try {
        if (xmpptcpConnection != null && xmpptcpConnection.isAuthenticated()) {
            addRequestFailureListener(listener);
            xmpptcpConnection.sendStanzaWithResponseCallback(stanza, new StanzaFilter() {
                @Override
                public boolean accept(Stanza response) {
                    if (response.getStanzaId() == null || stanza.getStanzaId() == null) {
                        return false;
                    }
                    return response.getStanzaId().equals(stanza.getStanzaId());
                }
            }, new StanzaListener() {
                @Override
                public void processPacket(final Stanza packet) throws SmackException.NotConnectedException, InterruptedException {
                    removeRequestFailureListener(listener);
                    if (xmpptcpConnection == null) {
                        Logger.e(TAG, "xmpptcpConnection is null, did not callBack");
                        return;
                    }                     if (listener != null) {
                        listener.onSuccess(packet);
                    }
                }
            }, new ExceptionCallback() {
                @Override
                public void processException(Exception exception) {
                    removeRequestFailureListener(listener);
                    if (listener != null && exception instanceof XMPPException.XMPPErrorException) {
                        XMPPException.XMPPErrorException xmppErrorException = (XMPPException.XMPPErrorException) exception;
                        listener.onFailure(new PacketException(xmppErrorException.getXMPPError().getCondition().name(), xmppErrorException));
                    } else if (listener != null && exception instanceof NoResponseException && stanza instanceof IQ) {
                        listener.onFailure(new PacketException(exception));
                    }
                }
            });
        } else if (listener != null) {
            listener.onFailure(new PacketException("XmpptcpConnection is null or no isAuthenticated"));
        }
    } catch (SmackException.NotConnectedException | InterruptedException | NullPointerException e) {
        removeRequestFailureListener(listener);
        Logger.e(TAG, "sendStanzaWithResponseCallback ", e);
        if (listener != null) {
            listener.onFailure(new PacketException(e.getClass().getSimpleName()));
        }
    }
}

I don’t see where cachedExecutorService is used in this code snippet.

And do you really need to use such a low level method as sendStanzaWithResponseCallback()? Usually there is a better method in Smack’s API for most use cases.

I print out the thread info in my project, it looks like:

So, I’m sure it used cachedExecutorService with my code.

Maybe you are right, that there is a better method. And I will go back to read the document right now.

And thanks for your answer.

A dump with the current stacktrace of all threads would be insightful.

I also encounter this problem. What can I do to solve this problem?

Read Flow’s message again. Provide a dump with a stacktrace.

image
Android Device Monitor Threads,The Smack-Cached Executor is increse

image

Which Smack version is this?

Smack version is 4.1.6

Please use the latest Smack release and report back if the issue still exists.

1 Like

Thanks for your answer

Hello, I’m encountering this problem as well with Smack version 4.2.4, particularly when messages are incoming to the device in hundreds within a short amount of time. Could you advice me on this matter?

I’m not sure what you would need from me to help me with this now that Android Studio has changed their approach with the Device Monitor, so do tell me the information you require for this matter.