1.应用场景/常见内容溢出问题
常见问题为内存溢出,分为堆内存溢出、非堆内存溢出,比较常见的为堆内存溢出,后2类属于非堆内存溢出。
堆溢出:
java.lang.OutOfMemoryError:Java heap spcace
原因:项目运行阶段,new的对象过多,撑满了配置的最大内存,会出现该错误
解决方法:手动设置Xms ,Xmx 的大小.堆的最大值设置为物理可用内存的最大值的80%
栈溢出:
java.lang.StackOverflowError
原因:通常都是某个代码逻辑递归层次太多导致的,
解决方法:修改递归代码,控制递归层数
方法区溢出:
java.lang.OutOfMemoryError:PermGen space
原因:开发的项目Java文件比较多的时候,会出现该错误(即项目很大,被JVM加载的文件很多)
解决方法:手动设置MaxPermSize大小.
2.虚拟内存JVM设置
内存大小的单位可以是m或者G,一般将初始值与最大值设置为一样,避免频繁调整内存分配而浪费系统资源。
设置堆的大小是非常重要的,不能太小哦啊,也不能太大。
Linux下Tomcat的JVM设置:
catalina.sh中,位置cygwin=false前,增加
JAVA_OPTS="$JAVA_OPTS -server -Xms92G -Xmx92G -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$CATALINA_HOME/logs/heap.dump"
内存设置说明
堆内存设置:-Xms 初始堆大小。如:-Xms256m或2G,默认为物理内存1/64。
-Xmx 最大堆大小。如:-Xmx512m或2G,默认为物理内存1/4。
非堆内存设置:
-XX:PermSize 永久代(方法区)的初始大小,默认为物理内存1/64。
-XX:MaxPermSize 永久代(方法区)的最大值,默认为物理内存1/4。
3、jvm的内存区域
java8的堆,有Eden、from、to、old四部分组成。组成比例是Eden:from :to = 8:1:1
新生代=Eden+from+to。老年代:新生代 = 2 : 1
总结下对象分配策略:
大多数情况下,对象在新生代 Eden 区中分配。当 Eden 区没有足够空间分配时,虚拟机将发起一次 Minor GC。
经过 Minor GC 后仍然存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并将对象年龄设为 1,对象在 Survivor区中每熬过一次 Minor GC,年龄就增加 1,当它的年龄增加到一定程度(并发的垃圾回收器默认为 15),CMS 是 6 时,就会被晋升到老年代中。
大对象直接进入老年代。HotSpot 虚拟机提供了-XX:PretenureSizeThreshold 参数,指定大于该设置值的对象直接在老年代分配,这样做的目的就是避免在 Eden 区及两个 Survivor区之间来回复制,产生大量的内存复制操作。
4、内存设置说明
优化连接数,主要是在conf/server.xml配置文件中进行修改
4.1、优化线程数
找到Connector port=“8080” protocol=“HTTP/1.1”,增加maxThreads和acceptCount属性(使acceptCount大于等于maxThreads),如下:
<Connector port=“8080” protocol="HTTP/1.1"connectionTimeout=“20000” redirectPort="8443"acceptCount=“500” maxThreads=“400” />
其中:
• maxThreads:tomcat可用于请求处理的最大线程数,默认是200
• minSpareThreads:tomcat初始线程数,即最小空闲线程数
• maxSpareThreads:tomcat最大空闲线程数,超过的会被关闭
• acceptCount:当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理.默认100
问题?
如果系统同时大概300人使用,设置多少合适,maxThreads 、minSpareThreads、acceptCount、connectionTimeout?
当系统需要支持大约 300 个用户同时使用时,可以根据系统的负载情况和性能需求来配置 Tomcat 的相关参数。以下是一些建议的配置:
-
maxThreads:对于支持大约 300 个用户的系统,可以考虑将
maxThreads
设置为 400 到 500 之间。这样可以确保系统具有足够的线程来处理用户请求,同时留有一定的缓冲以应对突发高峰。 -
minSpareThreads:
minSpareThreads
参数用于定义线程池中保持活动状态的最小线程数。建议设置为一个较小的值,例如 50 到 100,以确保在低负载情况下仍然有一定数量的线程可用。 -
acceptCount:
acceptCount
参数用于定义等待处理的连接请求的最大队列长度。对于支持大量用户的系统,可以将acceptCount
设置得稍微高一些,例如 200 或 300,以减少因瞬时爆发连接请求而导致的连接超时。 -
connectionTimeout:
connectionTimeout
参数定义了连接的超时时间,即连接在空闲多久后被关闭。可以根据系统的网络延迟和用户操作响应时间来设置合适的值,一般建议设置为几分钟到十几分钟之间。
综上所述,针对支持大约 300 个用户同时使用的系统,可以将 maxThreads
设置为 400 到 500,minSpareThreads
设置为 50 到 100,acceptCount
设置为 200 到 300,connectionTimeout
根据具体情况设置合适的超时时间。最终的配置参数取决于系统的具体需求和实际情况,可以通过监控系统性能和进行压力测试来进一步优化参数设置。
4.2、使用线程池
在server.xml中增加executor节点,然后配置connector的executor属性,如下:
<Executor name="tomcatThreadPool" namePrefix="req-exec-"maxThreads="1000" minSpareThreads="50"maxIdleTime="60000"/>
<Connector port="8080" protocol="HTTP/1.1"executor="tomcatThreadPool"/>
其中:
• namePrefix:线程池中线程的命名前缀
• maxThreads:线程池的最大线程数
• minSpareThreads:线程池的最小空闲线程数
• maxIdleTime:超过最小空闲线程数时,多的线程会等待这个时间长度,然后关闭
• threadPriority:线程优先级
注:当tomcat并发用户量大的时候,单个jvm进程确实可能打开过多的文件句柄,这时会报java.net.SocketException:Too many open files错误。可使用下面步骤检查:
• ps -ef |grep tomcat 查看tomcat的进程ID,记录ID号,假设进程ID为10001
• lsof -p 10001|wc -l 查看当前进程id为10001的 文件操作数
• 使用命令:ulimit -a 查看每个用户允许打开的最大文件数