Java 大型Xss设置如何影响服务器性能?

Java 大型Xss设置如何影响服务器性能?,java,Java,我有一个Java服务器,它在启动时读取一个大的序列化文件。这需要在启动时为主线程单独设置一个大的-Xss设置。所有处理服务器请求的线程都需要更少的堆栈空间。(Xss为20M) 这个(不断增加的)Xss值将如何影响服务器的内存使用?答案很复杂你也问错了问题-确保你把整个答案通读到底 回答您的问题:大型-Xss有多糟糕? JVM需要的RAM量基本上是堆+元空间+最大线程数*堆栈大小 堆很简单:这就是-Xmx参数的作用。元空间或多或少是一个常量(我过于简单化了),并不是特别大的数量 此外,假设这是通常

我有一个Java服务器,它在启动时读取一个大的序列化文件。这需要在启动时为主线程单独设置一个大的-Xss设置。所有处理服务器请求的线程都需要更少的堆栈空间。(Xss为20M)


这个(不断增加的)Xss值将如何影响服务器的内存使用?

答案很复杂你也问错了问题-确保你把整个答案通读到底

回答您的问题:大型
-Xss
有多糟糕? JVM需要的RAM量基本上是堆+元空间+最大线程数*堆栈大小

堆很简单:这就是
-Xmx
参数的作用。元空间或多或少是一个常量(我过于简单化了),并不是特别大的数量

此外,假设这是通常的服务器设置,您在其中设置了JVM获得静态内存量(它是一台服务器-它有一定数量的RAM,最好的选择通常是全部花费。为系统上运行的每个主要进程提供一个锁定配置的RAM。如果JVM是唯一运行在该系统上的主要软件(例如,涉及一个数据库,但它运行在另一台机器上),然后给JVM~7GB。为什么不呢?使用
-Xmx
和-Xms`,设置相同的值,并使其变大。如果postgres也在其上运行,则给JVM大约3GB和postgres 4GB(当然,这取决于你的应用程序有多大的db)。等等

关键是,如果您有一个较大的stacksize和一个相当大的max threads,比如说一个20MB的
-Xss
,最大线程数为100,那么您将丢失分配的7个2GB:在一个安装了8GB的盒子上,并且只有JVM是资源的主要消耗者,此设置:

java -Xmx7g -Xms7g -Xss20m
完全错误,并导致各种各样的麻烦-总计9GB,而我甚至还没有开始考虑元空间或内核的需求。这个盒子没有那么多!相反,你应该做的可能是
-Xmx5g-Xms5g-Xss20m

现在你知道了性能成本是什么了:成本是必须将
-Xmx-Xms
的值从7降低到5。如果你不得不将它从3降到1,情况会变得更糟,因为它是一个4GB的盒子-在这一点上,你所做的基本上是不可能的,除非你首先启动一个有更多ram的新服务器

实际上帮助你解决你的问题 忘记上面所有的,这是解决这个问题的错误方法。保持你的
-Xss
漂亮和低,或者不要设置它

取而代之的是,获取您的init代码并将其隔离,然后在单独设置的线程中运行它(然后在该线程上运行
.join()
,等待它完成并刷新您修改的init代码的所有字段;yield()根据需要设置HB/HA)。使用此线程构造函数:

Runnable initCode = () -> {
   // your init stuff goes here
};

ThreadGroup tg = Thread.currentThread().getThreadGroup();
Thread initThread = new Thread(tg, runnable, "init", 20L * 1024L * 1024L);
initThread.start();
initThread.join();
但是,先做一些研究。线程的API设计得很糟糕,并且会犯各种严重错误。特别是堆栈大小(这里是20MB)只是一个提示,javadoc说任何虚拟机都可以完全忽略它。好的API设计当然会指定,如果虚拟机无法执行请求的堆栈大小,则会引发异常

我做了一个快速测试;在mac上采用OpenJDK 11似乎没有问题

以下是我的测试设置:

> cat Test.java
public class Test {
        public static void main(String[] args) throws Exception {
                Runnable r = () -> {
                        System.out.println("Allowed stack depth: " + measure());
                };
                r.run();
                r.run();
                Thread t = new Thread(Thread.currentThread().getThreadGroup(), r, "init", 1024L * 1024L);
                t.start();
                t.join();
                r.run();
        }

        public static int measure() {
                int min = 1;
                int max = 50000;
                while (min < max) {
                        int mid = (max + min) / 2;
                        try {
                                attempt(mid);
                                if (min == mid) return min;
                                min = mid;
                        } catch (StackOverflowError e) {
                                max = mid;
                        }
                }
                return min;
        }

        public static void attempt(int depth) {
                if (depth == 0) return;
                attempt(depth - 1);
        }
}

> javac Test.java; java -Xss200k Test
Allowed stack depth: 2733
Allowed stack depth: 6549
Allowed stack depth: 49999
Allowed stack depth: 6549
>cat Test.java
公开课考试{
公共静态void main(字符串[]args)引发异常{
可运行r=()->{
System.out.println(“允许的堆栈深度:+measure());
};
r、 run();
r、 run();
线程t=新线程(Thread.currentThread().getThreadGroup(),r,“init”,1024L*1024L);
t、 start();
t、 join();
r、 run();
}
公共静态int度量(){
int min=1;
int max=50000;
同时(最小值<最大值){
int mid=(最大+最小)/2;
试一试{
尝试(中);
如果(min==mid)返回min;
最小=中等;
}捕获(堆栈溢出错误e){
最大值=中间值;
}
}
返回最小值;
}
公共静态无效尝试(整数深度){
如果(深度==0)返回;
尝试(深度-1);
}
}
>java-Xss200k测试
允许堆叠深度:2733
允许堆叠深度:6549
允许堆叠深度:49999
允许堆叠深度:6549
您无法检查堆栈跟踪的大小,因为JVM有一个硬限制,并且不会存储超过1024个堆栈跟踪元素,因此需要对答案进行二进制搜索

我不能很好地解释为什么这个值不是常数(它从2733跳到6549),甚至是为什么150k的
-Xss
会产生更高的实际值,这到底是怎么回事???-我会在发布这个答案后马上问一个问题,但它确实表明,使用更大堆栈生成的线程确实让您拥有更深的方法调用堆栈


使用目标JDK在目标环境中运行此测试代码,以确保其正常工作,然后您就有了实际的解决方案:)

谢谢。具有堆栈大小的线程构造函数正是我所缺少的。