Dozens of "HttpClient-N-SelectorManager" threads created
Daniel Fuchs
daniel.fuchs at oracle.com
Thu Jan 23 19:16:30 UTC 2025
Hi Ruslan,
There is only 1 SelectorManager thread per HttpClient instance.
If you see HttpClient-18-SelectorManager, it's because at
least 18 (or 19?) HttpClient instances have been created.
Prior to JDK 21, the SelectorManager thread needs two conditions
before it can exit:
1. All strong references to the related HttpClient instance
must have been released by the caller code
2. All HTTP operations must have completed
Note that if you use things like:
- BodyHandler.ofInputStream()
- BodyHandler.ofLines()
then failing to close the returned InputStream or Stream
will cause a leak that will prevent the SelectorManager
thread from exiting.
So you must make sure to always close these - even if
a non-200 status is received.
Starting from JDK 21, the HttpClient has been made AutoCloseable,
and offers methods such as close(), shutdown(), shutdownNow(),
and awaitTermination(), to make it possible to explicitely
close the client when you no longer needs it.
Also the documentation with regard to closing the HttpClient
or releasing its resources has been improved.
See
https://docs.oracle.com/en/java/javase/23/docs/api/java.net.http/java/net/http/HttpClient.html#streaming
for more details.
best regards,
-- daniel
On 23/01/2025 10:30, Ruslan Ibragimov wrote:
> I encounter strange behavior of java.net.http.HttpClient in my
> application running in amazoncorretto:17.0.13, after some time instead
> of just one HttpClient-1-SelectorManager thread I'm starting observing
> dozens of such threads.
>
> I'm creating only one instance of HttpClient in whole application, and
> confirming with logging that following snippet called only once:
>
> val httpClient = HttpClient.newBuilder()
> .version(HttpClient.Version./HTTP_2/)
> .followRedirects(HttpClient.Redirect./NEVER/)
> .connectTimeout(Duration.ofSeconds(60))
> .build()
>
>
> I also verified that no dependencies using HttpClient under the hood.
>
> "HttpClient-18-SelectorManager" #20791 daemon prio=5 os_prio=0 cpu=31.77ms elapsed=3679.62s allocated=178K defined_classes=0 tid=0x00007f212c5dfce0 nid=0x541f runnable [0x00007f21993ee000]
> java.lang.Thread.State: RUNNABLE
> at sun.nio.ch.EPoll.wait(java.base at 17.0.13/Native Method)
> at sun.nio.ch.EPollSelectorImpl.doSelect(java.base at 17.0.13/EPollSelectorImpl.java:118)
> at sun.nio.ch.SelectorImpl.lockAndDoSelect(java.base at 17.0.13/SelectorImpl.java:129)
> - locked <0x000000008c488e38> (a sun.nio.ch.Util$2)
> - locked <0x000000008c488de8> (a sun.nio.ch.EPollSelectorImpl)
> at sun.nio.ch.SelectorImpl.select(java.base at 17.0.13/SelectorImpl.java:141)
> at jdk.internal.net.http.HttpClientImpl$SelectorManager.run(java.net.http at 17.0.13/HttpClientImpl.java:894)
>
> Locked ownable synchronizers:
> - None
>
>
>
> So now I'm in situation when I see a lot (a few times they grow to 150+
> threads) of such threads created over time and stay alive. I believe
> it's also hurting application performance which lead even to timeouts on
> http stack (but this is unconfirmed).
>
> Any hint to understand why this might happen and how to debug root cause
> would be appreciated.
>
> --
> Best regards,
> Ruslan Ibragimov
> https://heapy.io/ <https://heapy.io/>
>
More information about the net-dev
mailing list