I am using threads in my Java application to partially get the data (using network calls). I have a method (which is not in a threaded class), which creates a thread pool with given size (10-15 maximum) and uses them for network calls, and I am calling that method several times from a loop.
When I am running this application on a slow machine (3 GB RAM, Pentium-IV), everything works fine, but when I run it on an iMac (32 GB RAM, i7 processor), it is creating too many threads, about 2,500 sometimes and throwing an out-of-memory error.
I suspect that the JVM
is not putting back the completed threads back in to the pool as soon they are finished, hence it's creating new threads.
And even on the iMac
, if I keep Thread.sleep(1000); in the for
loop which I mentioned above everything works fine. Creating about 900 threads though.
Below are the code samples from this application:
public ArrayList<String> getValuesForKeyFromMaps(String key, ArrayList<Meta> locations) throws InterruptedException, ExecutionException {
int threadNum = locations.size(); // 10-15 at max
ExecutorService executor = Executors.newFixedThreadPool(threadNum);
List<FutureTask<ArrayList<String>>> taskList = new ArrayList<FutureTask<ArrayList<String>>>();
for(final Meta location : locations){
FutureTask<ArrayList<String>> futureTask_1 = new FutureTask<ArrayList<String>>(new Callable<ArrayList<String>>() {
public ArrayList<String> call() throws Exception {
// Service call
return getValues(key, location);
}
});
taskList.add(futureTask_1);
executor.execute(futureTask_1);
}
ArrayList<String> values = new ArrayList<String>();
// Wait until all results are available and combine them at the same time
for (int j = 0; j < threadNum; j++) {
FutureTask<ArrayList<String>> futureTask = taskList.get(j);
values.addAll(futureTask.get());
}
executor.shutdown();
return values;
}
If I call the above method using below for loop on iMac, it throws a memory error, since it's creating about 2,500 threads. But it works fine on the slower machine.
for(String key : keySet){
getValuesForKeyFromMaps(key, metaMap.get(key));
}
And, with the below code, on iMac
it's working fine with about 900 threads.
for(String key : keySet) {
getValuesForKeyFromMaps(key, metaMap.get(key));
Thread.sleep(200); // Sleeping for 200 ms
}
If I increase sleep time in the above for loop to 1000 ms,
it's creating only 30-50 threads and the application is working fine.
How do I control maximum threads allowed in my application? I am intending to create/use 10-15 threads at the maximum at a given time, but Java is creating too many.
Why Do Too Many Threads Hurt App Performance? The impact of having too many threads comes in two ways. First, partitioning a fixed amount of work among too many threads gives each thread too little work that the overhead of starting and terminating threads swamps the useful work.
By the way in windows you can't create more then 1000 since windows can't handle the threads very well, but you can create 1000 threads in linux if you leverage with NPTL. So many persons told you java couldn't handle large concurrent job processings that wasn't 100% true.
Each JVM server can have a maximum of 256 threads to run Java applications.
Apart from -XX:+UseSerialGC which disables Parallel or Concurrent GC, there are the following options to reduce the number of JVM threads: -XX:CICompilerCount=1 leaves only one JIT compiler thread. -XX:+ReduceSignalUsage disables Signal Dispatcher thread. E.g. JVM will not handle SIGQUIT to dump threads.
It's not Java that's creating too many threads; you are!
Don't create an executor each time you call a function. If you have 100 collections with 100 elements each, you will create 10,000 threads - that is very resource-consuming... And pointless.
ExecutorService executor = Executors.newFixedThreadPool(threadNum);
You have, most likely, 8 cores - just create one executor with 8 threads and use it everywhere. Your code will work faster and your application will consume less, much much fewer resources.
Familiarize yourself with this code review singleton executor question. You may be able to use that solution in your application.
By using ExecutorService executor = Executors.newFixedThreadPool(threadNum);
you are creating a new thread pool for each call of getValuesForKeyFromMaps
. So when your keySet
contains 100 entries you'll end up with 100 pools with 10-15 threads each. Keep one thread-pool as an instance- or class-variable and use it whenever needed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With