Every thread created in a Java program has its own stack space. The stack space used is not allocated from the heap. Infact if you look at the OS report on the memory used by your JVM, you may notice that it is more than what -Xmx parameter specifies. This is because, beside other things, memory is used for the thread stacks too. And this memory is not included in the heap specified by the -Xms and -Xmx switches.
The thread stack is used to push stacks frames in nested method calls. If the nesting is so deep that the thread runs out of space, the thread dies with a StackOverflowError.
The default thread stack size varies with JVM, OS and environment variables. A typical value is 512k. It is generally larger for 64bit JVMs because references are 8 bytes rather than 4 bytes in size. This means that if your app uses 150 threads, 75MB will be used for thread stacks. In some environments the defaults stack may be as large as 2MB. With a large number of threads, this can consume a significant amount of memory which could otherwise be used by your application or OS.
In most applications, 128k happens to be enough for the stack. What you really need to do is adjust and observe. If you don’t see your app running out of stack space, use the -Xss JVM parameter to specify a smaller stack (-Xss128k).
Note that it is entirely possible that your OS rounds up values for stack size specified by your -Xss parameter. Watch out for that.
Using the program below, you can see how stack space is used up by methods with varying number of arguments. You’ll get the StackOverflowError with fewer nested method calls for a smaller stack. You could try adjusting the number of arguments and even the type of arguments for different behaviour.
By adjusting the stack size, and keeping the code the same, you can see the JVM dying at different points in the recursive call.
(code of Recursive method call)