Difference between stackSize of Thread and - Xss parameter

Keywords: Java jvm less

The stackSize and Xss parameters of Thread can control the stack memory size of a Thread. Do you know the difference between them? When these two configurations exist at the same time, which one shall prevail?
If you don't know the answer, read on.

stackSize of Thread

What is the stackSize of Thread?

  • The stackSize parameter can be passed in the constructor of Thread. If not, the default is 0. Its function is to control the size of stack memory allocated by the jvm to threads.
  • The relationship between stackSize and stack height (the number of nested layers of method calls) and the number of simultaneous threads is related to the JVM platform. Some platforms have invalid parameters. How it is implemented is up to the JVM.
  • In HostSpotVM, when the value is large, it may increase the depth of the stack in the thread; when the value is small, it may increase the number of simultaneous threads to avoid OutOfMemoryError (or other errors).
  • If this value is smaller than the minimum value specified by the JVM, take the default value of the JVM, which is generally 1M.
  • I tried to set the maximum value to 2G-1, and the stack depth reached 130 million before overflow, so I didn't find the upper limit of this value, but I think a thread allocation of 2G has been very abnormal.
  • Of course, stack depth is also related to the stack frame size. Stack depth = stackSize / average stack frame size.

JVM parameter - Xss

The - Xss parameter of the JVM also controls the memory size of a single stack

Difference

  1. -Xss is a global setting, which is a common setting for all threads. When it is not set, it has a default value of 1M. stackSize is a property of a thread, which only works on a thread.
  2. When stackSize is set to < = 0, the - Xss shall prevail (generally set to 0, and the DefaultThreadFactory provided by java is set to 0).
  3. When stackSize is set to be in the (0, 4k] range, the set - Xss will fail, and the stack space will take the default value of 1M. This is especially easy to get wrong!
  4. When stackSize is set to be in the (4k, 64k] range, the set - Xss will fail and the stack space will be taken as 4k.
  5. When stackSize is set to belong to the (64k, 128k] range, the set - Xss will fail, and the stack space is 64k.
  6. When the stackSize is set to be > 128K, the - Xss set will fail, and the stackSize itself will be taken as the stack space.

In summary, if stackSize < = 0, the JVM will assume that you have not set stackSize, so - Xss will prevail.
If stackSize > 0, the JVM takes stackSize as its criterion and ignores the - Xss parameter. However, the JVM will process the stackSize. When it is less than or equal to 4k, it is considered too small. Instead of using the default value (1M), it also does some gradient processing.

Be careful:

  • If there is no special requirement, try not to modify the stackSize parameter, because its function and scope depend on the platform. When migrating across platforms, you need to check whether you need to modify this parameter.
  • stackSize can also be used to control the relationship between thread number and stack depth. For example, some algorithms are implemented recursively, with a large stack depth.
  • The default values for individual stack memory sizes in this article may vary across JVM platforms.

The test code is as follows:

public class JVMStackTest {
    int count = 0;

    /**
     * java.lang.StackOverflowError
     * stack height:18562
     *
     * @author Shuaijun He
     */
    public void testStack() {
        this.count++;
        this.testStack();
    }

    /**
     * Add parameter
     * java.lang.StackOverflowError
     * stack height:17478
     *
     * @author Shuaijun He
     * @param a
     * @param b
     */
    public void testStack(int a, int b) {
        this.count++;
        this.testStack(a, b);
    }

    /**
     * Increase the number of local variables
     * java.lang.StackOverflowError
     * stack height:7845
     *
     * -Xss=100k Time stack height:665
     *
     * @author Shuaijun He
     * @param a
     * @param b
     */
    public void testStackWithLocalVar(int a, int b) {
        int c = 5;
        long d = 4L;
        System.out.println(c + d);
        this.count++;
        this.testStackWithLocalVar(a, b);
    }

    /**
     * Thread.stackSize = 2g-1 Time stack height:134191969
     * Thread.stackSize = 128m Time stack height:8204399 (delete System.out.println(c + d) if this line is up; otherwise, it is too slow)
     * Thread.stackSize = 4m Time stack height:32408
     * Thread.stackSize = 2m Time stack height:16028
     * Thread.stackSize = 1.5m Time stack height:11936
     * Thread.stackSize = 1m+1 Time stack height:8353
     * Thread.stackSize = 1m Time stack height:7833
     * Thread.stackSize = 512k+1 Time stack height:4242
     * Thread.stackSize = 512k Time stack height:3738
     * Thread.stackSize = 256k Time stack height:1687 (Set - Xss512k, and the stack height value remains unchanged)
     * Thread.stackSize = 128k+1 Time stack height:1168
     *
     * Thread.stackSize = 128k Time stack height:661
     * Thread.stackSize = 112k Time stack height:660
     * Thread.stackSize = 96k Time stack height:656
     * Thread.stackSize = 80k Time stack height:669
     * Thread.stackSize = 72k Time stack height:659
     * Thread.stackSize = 64k+1 Time stack height:662
     *
     * Thread.stackSize = 64k Time stack height:156
     * Thread.stackSize = 32k Time stack height:150
     * Thread.stackSize = 16k Time stack height:156
     * Thread.stackSize = 8k Time stack height:150
     * Thread.stackSize = 5k Time stack height:156
     * Thread.stackSize = 4k+1 Time stack height:146
     *
     * Thread.stackSize = 4k Time stack height:7837
     * Thread.stackSize = 2k Time stack height:7838
     * Thread.stackSize = 1k Time stack height:7831
     * Thread.stackSize = 1 Time stack height:7836 (Set - Xss512k, stack height= 7834)
     *
     * Thread.stackSize = 0 Time stack height:7839 (Set - Xss512k, stack height= 3737)
     * Thread.stackSize = -1 Time stack height:7835 (Set - Xss512k, stack height= 3737)
     * Conclusion:
     * stackSize <= 4k This parameter will be ignored and the default value is 1m.
     * stackSize Of (4k, 64k), take 4k
     * stackSize Belong to (64k, 128k), take 64k
     * stackSize For (128k, Max), take stackSize. No limit found!
     * Note: when the - Xss parameter and Thread.stackSize > 0 are set at the same time, the Thread.stackSize shall prevail; when the Thread.stackSize < = 0, the - Xss shall prevail.
     *
     */
    public static void testMyThreadStackSize(){
        // Set Thread.stackSize = 100k
        MyThreadFactory.getMyThreadFactory().newThread(()->{
            JVMStackTest test = new JVMStackTest();
            try {
                test.testStackWithLocalVar(1, 2);
            } catch (Throwable e) {
                System.out.println(e);
                System.out.println("stack height: " + test.count);
                try {
                    Field stackSizeField = Thread.class.getDeclaredField("stackSize");
                    stackSizeField.setAccessible(true);
                    long stackSize = stackSizeField.getLong(Thread.currentThread());
                    System.out.println("stackSize(kb): " + (stackSize / 1024) + ", " + stackSize);
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
        }).start();
    }

    /**
     * Test - impact of Xss
     */
    private static void jvmXss(){
        JVMStackTest test = new JVMStackTest();
        try {
//            test.testStack();
//            test.testStack(1, 2);
            test.testStackWithLocalVar(1, 2);
        } catch (Throwable e) {
            System.out.println(e);
            System.out.println("stack height:" + test.count);
        }
    }

    public static void main(String[] args) {

//        jvmXss();
        testMyThreadStackSize();
    }

}

Custom thread factory:

public class MyThreadFactory implements ThreadFactory {

    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;

    private static MyThreadFactory myThreadFactory = new MyThreadFactory();

    private MyThreadFactory() {
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() :
                Thread.currentThread().getThreadGroup();
        namePrefix = "pool-" +
                poolNumber.getAndIncrement() +
                "-thread-";
    }

    public static MyThreadFactory getMyThreadFactory() {
        return myThreadFactory;
    }

    @Override
    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r,
                namePrefix + threadNumber.getAndIncrement(),
                 -1);
        if (t.isDaemon()) {
            t.setDaemon(false);
        }
        if (t.getPriority() != Thread.NORM_PRIORITY) {
            t.setPriority(Thread.NORM_PRIORITY);
        }
        return t;
    }


}

Posted by mcovalt on Fri, 25 Oct 2019 20:24:06 -0700