【JVM原理探索】Thread的stackSize与-Xss参数的区别

简介: 【JVM原理探索】Thread的stackSize与-Xss参数的区别

前提概要


Thread的stackSize与-Xss参数都可以控制单个线程的栈内存大小,它们的区别你知道吗?当这两个配置同时存在时,以哪个为准




Thread 的 stackSize


  • 在Thread的构造器中可以传入stackSize参数。如果不传的话,默认是0。它的作用是控制JVM给线程分配栈内存的大小
  • stackSize与栈深度(stack height,就是方法内调方法的套嵌层数)和同时存在的线程数的关系是与JVM平台相关的,有些平台这个参数无效。具体怎么实现由JVM决定



在HotSpot VM中,值较大时可能会加大线程内栈的深度;值较小时可能加大同时存在的线程数,以避免出现OutOfMemoryError(或者其他Error)



  • 如果这个值比JVM规定的最小值还小的话,取JVM的默认值,一般是1M
  • 这个值我尝试过最大的设置为2G-1,栈深度达到1.3亿才溢出,所以说我没有找到这个值的上限,但是我觉得一个线程分配2G已经是很变态了
  • 当然栈深度也与栈帧的大小有关,栈深度=stackSize/平均栈帧大小





JVM参数-Xss


JVM的-Xss参数也是控制单个栈内存大小的参数




区别


-Xss是全局的,也就是所有线程共同的设置,当不设置时有默认值,一般为1M。而stackSize是某个线程的属性,只作用在某个线程上



  • 当设置stackSize属于<=0 时,以-Xss为准(一般设置为0即可,java提供的DefaultThreadFactory就是设置的0)
  • 当设置stackSize属于(0, 4k]区间时,设置的-Xss会失效,栈空间取默认值1M。
  • 当设置stackSize属于(4k, 64k]区间时,设置的-Xss会失效,栈空间取4k。
  • 当设置stackSize属于(64k, 128k]区间时,设置的-Xss会失效,栈空间取64k
  • 当设置stackSize属于 >128k 时,设置的-Xss会失效,栈空间取stackSize本身



总之,如果 stackSize<=0 ,JVM会认为你没有设置 stackSize,所以以 -Xss 为准。如果 stackSize > 0, JVM 会以 stackSize 为准, 而忽略 -Xss参数。只不过JVM会对stackSize作处理,当小于等于4k时,认为太小了,用默认值(1M)代替,另外也做了一些梯度处理




注意


stackSize 参数,如果没有特殊的需求,尽量不要修改,因为它本身的作用和范围取决于平台,在跨平台迁移时,需要检查是否需要修改这个参数



stackSize 用于精细地控制线程数和栈深度的关系,也是有用武之地的。比如有些算法用递归实现,栈深度较大。

本文中的单个栈内存大小的默认值在不同JVM平台上可能会有所不同。



测试代码如下:

public class JVMStackTest {
    int count = 0;
    /**
     * java.lang.StackOverflowError
     * stack height:18562
     *
     * @author Shuaijun He
     */
    public void testStack() {
        this.count++;
        this.testStack();
    }
    /**
     * 增加参数
     * 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);
    }
    /**
     * 增加局部变量 数量
     * java.lang.StackOverflowError
     * stack height:7845
     *
     * -Xss=100k 时 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 时 stack height:134191969
     * Thread.stackSize = 128m 时 stack height:8204399 (此行往上,删掉System.out.println(c + d);否则太慢了)
     * Thread.stackSize = 4m 时 stack height:32408
     * Thread.stackSize = 2m 时 stack height:16028
     * Thread.stackSize = 1.5m 时 stack height:11936
     * Thread.stackSize = 1m+1 时 stack height:8353
     * Thread.stackSize = 1m 时 stack height:7833
     * Thread.stackSize = 512k+1 时 stack height:4242
     * Thread.stackSize = 512k 时 stack height:3738
     * Thread.stackSize = 256k 时 stack height:1687 (设置 -Xss512k, stack height值不变)
     * Thread.stackSize = 128k+1 时 stack height:1168
     *
     * Thread.stackSize = 128k 时 stack height:661
     * Thread.stackSize = 112k 时 stack height:660
     * Thread.stackSize = 96k 时 stack height:656
     * Thread.stackSize = 80k 时 stack height:669
     * Thread.stackSize = 72k 时 stack height:659
     * Thread.stackSize = 64k+1 时 stack height:662
     *
     * Thread.stackSize = 64k 时 stack height:156
     * Thread.stackSize = 32k 时 stack height:150
     * Thread.stackSize = 16k 时 stack height:156
     * Thread.stackSize = 8k 时 stack height:150
     * Thread.stackSize = 5k 时 stack height:156
     * Thread.stackSize = 4k+1 时 stack height:146
     *
     * Thread.stackSize = 4k 时 stack height:7837
     * Thread.stackSize = 2k 时 stack height:7838
     * Thread.stackSize = 1k 时 stack height:7831
     * Thread.stackSize = 1 时 stack height:7836  (设置 -Xss512k, stack height= 7834)
     *
     * Thread.stackSize = 0 时 stack height:7839  (设置 -Xss512k, stack height= 3737)
     * Thread.stackSize = -1 时 stack height:7835 (设置 -Xss512k, stack height= 3737)
     * 总结:
     * stackSize <= 4k 会忽略此参数,取默认值 1m
     * stackSize 属于 (4k, 64k] 取4k
     * stackSize 属于 (64k, 128k] 取64k
     * stackSize 属于 (128k, Max) 取stackSize。没找到上限!
     * 注意:同时设置 -Xss 参数和 Thread.stackSize > 0时,以Thread.stackSize为准;Thread.stackSize <=0 时, 以 -Xss 为准
     *
     */
    public static void testMyThreadStackSize(){
            // 设置 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();
    }
    /**
     * 测试 -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();
    }
}
复制代码




自定义线程工厂

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;
    }
}




目录
打赏
0
0
0
0
379
分享
相关文章
JVM实战—4.JVM垃圾回收器的原理和调优
本文详细探讨了JVM垃圾回收机制,包括新生代ParNew和老年代CMS垃圾回收器的工作原理与优化方法。内容涵盖ParNew的多线程特性、默认线程数设置及适用场景,CMS的四个阶段(初始标记、并发标记、重新标记、并发清理)及其性能分析,以及如何通过合理分配内存区域、调整参数(如-XX:SurvivorRatio、-XX:MaxTenuringThreshold等)来优化垃圾回收。此外,还结合电商大促案例,分析了系统高峰期的内存使用模型,并总结了YGC和FGC的触发条件与优化策略。最后,针对常见问题进行了汇总解答,强调了基于系统运行模型进行JVM参数调优的重要性。
JVM实战—4.JVM垃圾回收器的原理和调优
JVM实战—1.Java代码的运行原理
本文介绍了Java代码的运行机制、JVM类加载机制、JVM内存区域及其作用、垃圾回收机制,并汇总了一些常见问题。
JVM实战—1.Java代码的运行原理
JVM实战—5.G1垃圾回收器的原理和调优
本文详细解析了G1垃圾回收器的工作原理及其优化方法。首先介绍了G1通过将堆内存划分为多个Region实现分代回收,有效减少停顿时间,并可通过参数设置控制GC停顿时长。接着分析了G1相较于传统GC的优势,如停顿时间可控、大对象不进入老年代等。还探讨了如何合理设置G1参数以优化性能,包括调整新生代与老年代比例、控制GC频率及避免Full GC。最后结合实际案例说明了G1在大内存场景和对延迟敏感业务中的应用价值,同时解答了关于内存碎片、Region划分对性能影响等问题。
JVM原理与实现——Synchronized关键字
在多线程Java程序中,`Synchronized`关键字用于确保线程安全。本文深入探讨其工作原理,通过分析字节码`monitorenter`和`monitorexit`,解释JVM如何实现同步机制。文章展示了`Synchronized`方法的编译结果,并详细解析了轻量锁和重度锁的实现过程,包括Mark Word的状态变化及CAS操作的应用。最后简要介绍了`ObjectMonitor::enter()`函数在获取重度锁时的作用。
JVM原理与实现——Synchronized关键字
|
4月前
|
JVM内存参数
-Xmx[]:堆空间最大内存 -Xms[]:堆空间最小内存,一般设置成跟堆空间最大内存一样的 -Xmn[]:新生代的最大内存 -xx[use 垃圾回收器名称]:指定垃圾回收器 -xss:设置单个线程栈大小 一般设堆空间为最大可用物理地址的百分之80
|
4月前
|
深入解析:JDK与JVM的区别及联系
在Java开发和运行环境中,JDK(Java Development Kit)和JVM(Java Virtual Machine)是两个核心概念,它们在Java程序的开发、编译和运行过程中扮演着不同的角色。本文将深入解析JDK与JVM的区别及其内在联系,为Java开发者提供清晰的技术干货。
78 1
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程 ?
尼恩提示: G1垃圾回收 原理非常重要, 是面试的重点, 大家一定要好好掌握
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程  ?
JVM进阶调优系列(6)一文详解JVM参数与大厂实战调优模板推荐
本文详述了JVM参数的分类及使用方法,包括标准参数、非标准参数和不稳定参数的定义及其应用场景。特别介绍了JVM调优中的关键参数,如堆内存、垃圾回收器和GC日志等配置,并提供了大厂生产环境中常用的调优模板,帮助开发者优化Java应用程序的性能。
JVM知识体系学习七:了解JVM常用命令行参数、GC日志详解、调优三大方面(JVM规划和预调优、优化JVM环境、JVM运行出现的各种问题)、Arthas
这篇文章全面介绍了JVM的命令行参数、GC日志分析以及性能调优的各个方面,包括监控工具使用和实际案例分析。
284 3
【编程进阶知识】精细调控:掌握Eclipse JVM参数配置的艺术
本文详细介绍了如何在Eclipse中配置JVM参数,包括内存的初始和最大值设置。通过具体步骤和截图演示,帮助开发者掌握JVM参数的精细调控,以适应不同的开发和测试需求。
81 1