A few weeks ago, my team decided to start leveraging NewRelic JFR which is a relatively new feature from NewRelic to provide visibility on JFR events, and I noticed something that got me thinking 🤔
Who the hell is creating all these threads?
By the way, you don't need to use NewRelic or JFR to have a look at your threads. Since JVM threads are actually OS threads, you can also view them with ps
.
Simply, type ps -ef
to find the ID of your java process and then ps -T -p <pid>
.
In my case, it gave me the following output (redacted):
PID SPID TTY TIME CMD
2 2 ? 00:00:00 java
2 3 ? 00:00:08 java
2 4 ? 00:10:17 GC Thread#0
...
2 17 ? 00:10:17 GC Thread#1
2 34 ? 00:00:01 New Relic Jar A
...
2 50 ? 00:00:02 pool-3-thread-1
2 51 ? 00:00:02 redisson-netty-
...
2 89 ? 00:00:39 pool-5-thread-1
...
2 109 ? 01:16:09 pool-6-thread-1
Threads everywhere
As our applications grow larger, we start making use of libraries to make external web requests, database queries, etc. Often times, these libraries make use of threads (or thread pools) behind the scenes to improve their throughput.
The JVM assigns threads some default names like the ones we observed above (Thread-%
, pool-%-thread-%
).
Although these names make sense as default values, they are not as helpful when we try to understand if a particular set of threads is misbehaving.
This could be:
- Allocating too much memory.
- Taking too much CPU time.
- Spinning up too many threads (which also has a memory usage impact).
- Deadlocks.
Name your pets
Mike Wazowski once said:
In this case, I would recommend not to follow his advice and name your 🧵
This will save you time in the future since you will be able to use effectively many tools that will help you troubleshooting any performance issues.
How do I do that?
Threads
If you are the one creating the thread, you can simply do:
thread.setName("boo");
Pools
If you are creating the thread pool, the Executors
class allows you to pass a ThreadFactory
to its different factory methods.
If you combine that with Guava's ThreadFactoryBuilder
, you can easily make sure your threads are named with the following:
var factory =
new ThreadFactoryBuilder().setNameFormat("boo-%d").build();
var pool = Executors.newFixedThreadPool(1, factory);
Closing words
Once Project Loom lands on the SDK, things may change and this advice may not be as useful, since there will no longer be a 1-1 mapping between OS threads and JVM virtual threads.
However, that will take some time to be widely available and even more so to an LTS version (next LTS is planned for September 2023).
To end, I will go against the pet vs cattle DevOps's mindset and say:
Treat your threads as pets, not cattle ... for now
Top comments (0)