Odi's astoundingly incomplete notes
New entries | CodeJava stack size myths
How Java uses the stack is basically undocumented. So we developers are left with finding out ourselves. Today I wrote some interesting test code, that delivers surprising results. For my tests I used Sun JDK 1.5.0_12 under RedHat Enterprise Linux 3.
First I wanted to know the stack size that the VM chooses for threads. I found that the
I performed these tests by creating new threads and setting them asleep in a blocking wait call immediately. I kept creating threads until the VM ran out of memory. With 512k stacks the number of threads was around 3700, for 256k stacks around 7300, for 128k around 13700. That leads to the following memory consumption by the stacks:
Next I tested how deep I could call into a recursive method until I get stack overflows. I did this by modifying the initial program, so that each thread would recurse into a method until it hit stack overflow, then fall asleep. The thread recorded the maximum recursion depth. That's where I got really weird results. Especially with the server VM the maximum depth varies over a wide range between 1750 and 5700 calls (128k stacks)! That's far from constant, which would have been my first guess. With the client VM the number is generally lower but doesn't vary that much: between 1100 and 1650.
Also the maximum depth seems to be lower at the beginning of the test and increases toward the end.
If someone wants to repeat my tests, here is the test code that I was using. I am very interested to get further insights on how exactly stack works in Java.
First I wanted to know the stack size that the VM chooses for threads. I found that the
ulimit -s
parameter has no direct influence on the stack size chosen by the VM. The default is apparently 512kb, and can be changed freely with the -Xss
and -XX:ThreadStackSize
parameters. But I could not find a difference in behaviour between those parameters. They appear to do the same thing. Further I found that this Linux machine is able to create new threads at a rate of about 5000 per second.I performed these tests by creating new threads and setting them asleep in a blocking wait call immediately. I kept creating threads until the VM ran out of memory. With 512k stacks the number of threads was around 3700, for 256k stacks around 7300, for 128k around 13700. That leads to the following memory consumption by the stacks:
3700 x 512kB = 1850MB 7300 x 256kB = 1825MB 13700 x 128kB = 1713MBOf course a 32-bit process is limited to 4GB of address space (minus 1 or 2 GB for the kernel). So it is only natural that the memory is close to these 2GB minus the heap size. (Note that Stack is never allocated from the heap.)
Next I tested how deep I could call into a recursive method until I get stack overflows. I did this by modifying the initial program, so that each thread would recurse into a method until it hit stack overflow, then fall asleep. The thread recorded the maximum recursion depth. That's where I got really weird results. Especially with the server VM the maximum depth varies over a wide range between 1750 and 5700 calls (128k stacks)! That's far from constant, which would have been my first guess. With the client VM the number is generally lower but doesn't vary that much: between 1100 and 1650.
Also the maximum depth seems to be lower at the beginning of the test and increases toward the end.
If someone wants to repeat my tests, here is the test code that I was using. I am very interested to get further insights on how exactly stack works in Java.
On java 6 the test would likely not even finish, since it does tail recursion optimisation, meaning that your f(i) call would not take any space in the stack.
Cheers,
Paul
What am I missing?
Chris
total: used: free: shared: buffers: cached:
Mem: 4189683712 1384755200 2804928512 0 150441984 656486400
Swap: 2146754560 0 2146754560
MemTotal: 4091488 kB
MemFree: 2739188 kB
MemShared: 0 kB
Buffers: 146916 kB
Cached: 641100 kB
SwapCached: 0 kB
Active: 734156 kB
ActiveAnon: 428588 kB
ActiveCache: 305568 kB
Inact_dirty: 371072 kB
Inact_laundry: 111372 kB
Inact_clean: 0 kB
Inact_target: 243320 kB
HighTotal: 3276528 kB
HighFree: 2193932 kB
LowTotal: 814960 kB
LowFree: 545256 kB
SwapTotal: 2096440 kB
SwapFree: 2096440 kB
CommitLimit: 4142184 kB
Committed_AS: 3304968 kB
HugePages_Total: 0
HugePages_Free: 0
Hugepagesize: 2048 kB
Max. threads: 3329
Max. recursions: 26832
Min. recursions: 8304
Threads per second: 195.8593
Thanks
Amir
Where the GC print outs are coming from?
Thanks
Amir
Cheers,
Martin