常用的JVM参数
java启动参数共分为三类
- 标准参数(-),所有的JVM实现都必须实现这些参数的功能,而且向后兼容;
- 非标准参数(-X),默认jvm实现这些参数的功能,但是并不保证所有jvm实现都满足,且不保证向后兼容;
- 非Stable参数(-XX),此类参数各个jvm实现会有所不同,将来可能会随时取消,需要慎重使用;
-XX有两种格式:
(1) Boolean类型 格式:-XX:[+-]
-XX:+UseConcMarkSweepGc //表示启用CMS垃圾收集器
-XX:+UseG1Gc //表示启用G1垃圾收集器
(2) 非Boolean类型 格式:-XX:
-XX:MaxGcPauseMillis=500 //GC的最大停顿时间是500毫秒
-XX:GCTimeRatio=19 //设置吞吐量大小,它的值是一个 0-100 之间的整数。假设 GCTimeRatio 的值为 n,那么系统将花费不超过 1/(1+n) 的时间用于垃圾收集
常用的标准参数
-verbose:class
输出jvm载入类的相关信息,当jvm报告说找不到类或者类冲突时可此进行诊断。
-verbose:gc
输出每次GC的相关情况。
-verbose:jni
输出native方法调用的相关情况,一般用于诊断jni调用错误信息。
常用的非标准参数
扩展参数,比较有用:
-Xms512m
设置JVM促使内存为512m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xmx512m
设置JVM最大可用内存为512M。
-Xmn200m
设置年轻代大小为200M。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-Xss128k
设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
-Xloggc:file
与-verbose:gc功能类似,只是将每次GC事件的相关情况记录到一个文件中,文件的位置最好在本地,以避免网络的潜在问题。若与verbose命令同时出现在命令行中,则以-Xloggc为准。
-Xprof
跟踪正运行的程序,并将跟踪数据在标准输出输出;适合于开发环境调试。
非Stable(固定)参数
用-XX作为前缀的参数列表在jvm中可能是不健壮的,但是由于这些参数中的确有很多是对我们很有用的,比如我们经常会见到的-XX:PermSize、-XX:MaxPermSize等等。
1、行为参数如下:
-XX:-DisableExplicitGC
禁止调用System.gc();但jvm的gc仍然有效。
-XX:+MaxFDLimit
最大化文件描述符的数量限制
-XX:+ScavengeBeforeFullGC
新生代GC优先于Full GC执行
-XX:+UseGCOverheadLimit
在抛出OOM之前限制jvm耗费在GC上的时间比例
-XX:+UseConcMarkSweepGC
对老生代采用并发标记交换算法)进行GC
-XX:+UseParallelGC
启用并行GC
-XX:+UseParallelOldGC
对Full GC启用并行,当-XX:-UseParallelGC启用时该项自动启用
-XX:+UseSerialGC
启用串行GC
-XX:+UseThreadPriorities
启用本地线程优先级
以上红色加粗的三个参数代表着jvm中GC执行的三种方式,即串行、并行、并发;
串行(SerialGC)是jvm的默认GC方式,一般适用于小型应用和单处理器,算法比较简单,GC效率也较高,但可能会给应用带来停顿;
并行(ParallelGC)是指GC运行时,对应用程序运行没有影响,GC和app两者的线程在并发执行,这样可以最大限度不影响app的运行;
并发(ConcMarkSweepGC)是指多个线程并发执行GC,一般适用于多处理器系统中,可以提高GC的效率,但算法复杂,系统消耗较大;
2、性能调优参数
-XX:ParallelGCThreads=4
指定并行 GC 线程的数量(所以必须是并行GC收集器才有效),一般最好和 CPU 核心数量相当。默认情况下,当 CPU 数量小于8, ParallelGCThreads 的值等于 CPU 数量,当 CPU 数量大于 8 时,则使用公式:ParallelGCThreads = 8 + ((N - 8) * 5/8) = 3 +((5*CPU)/ 8);同时这个参数只要是并行 GC 都可以使用,不只是 ParNew。
由于GC操作会暂停所有的应用程序线程,JVM为了尽量缩短停顿时间就必须尽可能地利用更多的CPU资源。这意味着,默认情况下,JVM会在机器的每个CPU上运行一个线程,最多同时运行8个。一旦达到这个上限,JVM会调整算法,每超出5/8个CPU启动一个新的线程。所以总的线程数就是(这里的N代表CPU的数目):ParallelGCThreads = 8 + ((N - 8) * 5/8)
有时候使用这个算法估算出来的线程数目会偏大。如果应用程序使用一个较小的堆(譬如大小为1 GB)运行在一个八颗CPU的机器上,使用4个线程或者6个线程处理这个堆可能会更高效。在一个128颗CPU的机器上,启动83个垃圾收集线程可能也太多了,除非系统使用的堆已经达到了最大上限。
-XX:MaxGCPauseMillis=100
每次年轻代垃圾回收的最长时间(最大暂停时间)。
果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。
-XX:GCTimeRatio=100
设置垃圾回收时间占程序运行时间的百分比,公式为1/(1+n)
-XX:LargePageSizeInBytes=4m
设置用于Java堆的大页面尺寸
-XX:MaxHeapFreeRatio=70
GC后java堆中空闲量占的最大比例
-XX:MaxNewSize=size
新生成对象能占用内存的最大值
-XX:MaxPermSize=64m
老生代对象能占用内存的最大值
-XX:MinHeapFreeRatio=40
GC后java堆中空闲量占的最小比例
-XX:NewRatio=2
新生代内存容量与老生代内存容量的比例。如上表示新生代占1,老年代占2,新生代占1/3。
-XX:SurvivorRatio=8
定义了新生代中Eden区域和Survivor区域(From幸存区或To幸存区)的比例,默认为8,也就是说Eden占新生代的8/10,From幸存区和To幸存区各占新生代的1/10。
公式:
Eden = (R*Y)/(R+1+1) From = Y/(R+1+1) To = Y/(R+1+1)
R:SurvivorRatio比例
Y:新生代空间大小
举个例子,如果我们通过设置-Xmn60M来指定新生代分配的空间大小,那么Eden则会分配60M * 0.8 = 48M,Survivor一共分配60M * 0.2 = 12M的内存空间。
-XX:NewSize=2.125m
新生代对象生成时占用内存的默认值
-XX:ReservedCodeCacheSize=32m
保留代码占用的内存容量
-XX:ThreadStackSize=512
设置线程栈大小,若为0则使用系统默认值
-XX:+UseLargePages
使用大页面内存
说明:我们在日常性能调优中基本上都会用到以上红粗体的这几个属性;
3、调试参数
-XX:-CITime
打印消耗在JIT编译的时间
-XX:ErrorFile=./hs_err_pid
保存错误日志或者数据到文件中
-XX:+ExtendedDTraceProbes
开启solaris特有的dtrace探针
-XX:HeapDumpPath=./java_pid
指定导出堆信息时的路径或文件名
-XX:+HeapDumpOnOutOfMemoryError
当首次遭遇OOM时导出此时堆中相关信息
-XX:HeapDumpPath=e:\oom.dump
导出OOM文件的路径设置(一定得注意如果加了+号下面就会报错)
-XX:OnError="
出现致命ERROR之后运行自定义命令
-XX:OnOutOfMemoryError="
当首次遭遇OOM时执行自定义命令。
OutOfMemory自动重启程序
-XX:+PrintClassHistogram
遇到Ctrl-Break后打印类实例的柱状信息,与jmap -histo功能相同
-XX:+PrintConcurrentLocks
遇到Ctrl-Break后打印并发锁的相关信息,与jstack -l功能相同
-XX:+PrintCommandLineFlags
打印在命令行中出现过的标记
-XX:+PrintCompilation
当一个方法被编译时打印相关信息
-XX:+PrintGC
每次GC时打印相关信息
-XX:+PrintGCDetails
每次GC时打印详细信息
-XX:+PrintGCTimeStamps
打印每次GC的时间戳
-XX:+TraceClassLoading
跟踪类的加载信息
-XX:-TraceClassLoadingPreorder
跟踪被引用到的所有类的加载信息
-XX:+TraceClassResolution
跟踪常量池
-XX:+TraceClassUnloading
跟踪类的卸载信息
-XX:+TraceLoaderConstraints
跟踪类加载器约束的相关信息
Tomcat配置JVM参数
-Xms JVM初始化堆的大小
-Xmx JVM堆的最大值
这两个值的大小一般根据需要进行设置。初始化堆的大小执行了虚拟机在启动时向系统申请的内存的大小。一般而言,这个参数不重要。但是有的应用程序在大负载的情况下会急剧地占用更多的内存,此时这个参数就是显得非常重要,如果虚拟机启动时设置使用的内存比较小而在这种情况下有许多对象进行初始化,虚拟机就必须重复地增加内存来满足使用。
由于这种原因,我们一般把 -Xms 和 -Xmx 设为一样大,而堆的最大值受限于系统使用的物理内存。一般使用数据量较大的应用程序会使用持久对象,内存使用有可能迅速地增长。当应用程序需要的内存超出堆的最大值时虚拟机就会提示内存溢出,并且导致应用服务崩溃。因此一般建议堆的最大值设置为可用内存的最大值的80%。
Tomcat默认可以使用的内存为128MB,在较大型的应用项目中,这点内存是不够的,需要调大。
Windows下,在文件/bin/catalina.bat,Unix下,在文件/bin/catalina.sh前面,增加如下配置:
JAVA_OPTS='-Xms【初始化内存大小】-Xmx【可以使用的最大内存】'
如我们设置为:JAVA_OPTS'-Xms256m -Xmx512m'
表示初始化内存为256MB,可以使用的最大内存为512MB。
-Xms为tomcat启动初始内存,一般为服务器开机后可用空闲内存减去100M。
-Xmx为tomcat最大占用内存,一般为服务器开机后可用空闲内存减去50M。
另外需要考虑的是Java提供的垃圾回收机制。虚拟机的堆大小决定了虚拟机花费在收集垃圾上的时间和频度。收集垃圾可以接受的速度与应用有关,应该通过分析实际的垃圾收集的时间和频率来调整。
如果堆的大小很大,那么完全垃圾收集就会很慢,但是频度会降低。如果你把堆的大小和内存的需要一致,完全收集就快,但是会更加频繁。调整堆大小的的目的是最小化垃圾收集的时间,以在特定的时间内最大化处理客户的请求。在基准测试的时候,为保证最好的性能,要把堆的大小设大,保证垃圾收集不在整个基准测试的过程中出现。
如果系统花费很多的时间收集垃圾,请减小堆大小。一次完全的垃圾收集应该不超过3-5秒。如果垃圾收集成为瓶颈,那么需要指定代的大小,检查垃圾收集的详 细输出,研究垃圾收集参数对性能的影响。一般说来,你应该使用物理内存的80%作为堆大小。当增加处理器时,记得增加内存,因为分配可以并行进行,而垃圾收集不是并行的。
(1) 建议用64位操作系统,Linux下64位的jdk比32位jdk要慢一些,但是吃得内存更多,吞吐量更大。
(2) Xmx和Xms设置一样大,MaxPermSize和MinPermSize设置一样大,这样可以减轻伸缩堆大小带来的压力。
(3) 调试的时候设置一些打印JVM参数,如-XX:+PrintClassHistogram -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -Xloggc:log/gc.log,这样可以从gc.log里看出 一些端倪出来。
(4) 统停顿的时候可能是GC的问题也可能是程序的问题,多用jmap和jstack查看,或者killall -3 java,然后查看java控制台日志,能看出很多问题。有一次,网站突然很慢,jstack一看,原来是自己写的URLConnection连接太多没有释放,改一下程序就OK了。
(5) 仔细了解自己的应用,如果用了缓存,那么年老代应该大一些,缓存的HashMap不应该无限制长,建议采用LRU算法的Map做缓存,LRUMap的最大长度也要根据实际情况设定。