一、查看jvm常用命令
jinfo:可以输出并修改运行时的java 进程的opts。
jps:与unix上的ps类似,用来显示本地的java进程,可以查看本地运行着几个java程序,并显示他们的进程号。
jstat:一个极强的监视VM内存工具。可以用来监视VM内存内的各种堆和非堆的大小及其内存使用量。
jmap:打印出某个java进程(使用pid)内存内的所有'对象'的情况(如:产生那些对象,及其数量)。
jconsole:一个java GUI监视工具,可以以图表化的形式显示各种数据。并可通过远程连接监视远程的服务器VM。
需要注意:在使用这些工具前,先用JPS命令获取当前的每个JVM进程号,然后选择要查看的JVM。
二、jinfo
jinfo可以查看设置的jvm的信息
jinfo -flag ThreadStackSize [pid]
jinfo -flags [pid]
jinfo -flag UseConcMarkSweepGC [pid]
jinfo -flag UseG1GC [pid]
jinfo -flag UseParallelGC [pid]
三、查看jvm运行时的参数
得出的结果,如果是
= 表示默认值
:= 表示被用户或者jvm修改后的值
2)-XX:+UnlockDiagnosticVMOptions 解锁诊断参数
-XX:+PrintCommandLineFlags 打印命令行参数
3)jps
jps这个没什么好说的,在linux里面ps是查看进程,jps就是java的ps,查看java进程
如果执行"jps -l"就能查看整个类名
四、jstat
这个指令用来查看jvm统计信息,主要分以下三类:
jstat -class [pid] 1000 10
这个指令指的是每1秒(1000就是1000ms)查看一次类的装载信息,一共看十次。后面的1000和10可以不要
2)垃圾收集
-gc、-gcutil、-gccause、-gcnew、-gcold
3)JIT编译
jstat -compiler [pid]
jstat -printcompilation [pid]
五、jmap
jmap是一个很重要的命令,可以查看jvm内存使用情况。可以分为两点讲解:
内存溢出自动导出,设置参数既可:
+XX:HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=./
不用多说,设置内存溢出自动导出还有导出的路径
使用jmap命令手动导出:
jmap -dump:format=b,file=heap.hprof [pid]
其中format=b代表用二进制导出
2)查看内存映像文件:
在这里http://www.eclipse.org/mat/downloads.php下载内存分析工具,然后把dump文件导入分析就可以了。
六 、jstack
jstack也是一个很重要的命令,可以查看线程使用情况。这里需要知道现场的几种状态,参考如下:
- RUNNABLE状态:The thread is executing in the JVM.
- BLOCKED状态:The thread is blocked waiting for a monitor lock.
- WAITING状态:The thread is waiting indefinitely for another thread to perform a particular action.
- TIMED_WAITING状态:The thread is waiting for another thread to perform an action for up to a specified waiting time.
- TERMINATED状态:The thread has exited.
状态之间是怎么转换的呢?参考如下:
jvm里面查看线程使用情况的命令"jstack [pid]",将得到的线程情况输入一个叫做stack.txt的文件:
这个stack.txt文件能够查看到jvm里面某个id的线程是用来做什么的?如果发生了死锁,在最后也会列举出来。
在linux里面怎么知道线程线上执行的情况呢,哪些线程占用了很大的内存呢?
这时可以用这个命令 "top -p [pid] -H" 就能够看到单个进程里面线程的情况:
通过printf "%x" [pid] 可以将10精致的pid 转换为16进制,转换成16进制有什么用?
因为jstack出来的线程是十六进制的,通过刚刚那个值,就能够在jstack出来的文件里面找到使用内存最多的线程是哪一个线程了。
七、jvisualvm
jvisualvm是一个可视化工具,集成了上面指令的几乎所有功能,但是它更加直观,所以jvisualvm也很重要。它也在jdk目录的bin目录下。监控本地应用很简单,直接打开jvisualvm就能看到有本地的了。下面着重介绍监控远程的:
修改tomcat的Catalina.sh
JAVA_OPTS='-Dcom.sun.management.jmxremote.port=12345 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=192.168.1.54'
参数说明:
第一个参数是端口,不用说;
第二个参数是不需要ssl验证,链接的时候不勾那个属性
第三个参数是不需要验证
第四个参数是这个tomcat应用的地址,注意不是jvisualvm的地址。
2)监控远程java应用:
nohup java
-Dcom.sun.management.jmxremote
-Djava.rmi.server.hostname=192.168.1.54
-Dcom.sun.management.jmxremote.port=12344
-Dcom.sun.management.jmxremote.ssl=false -
Dcom.sun.management.jmxremote.authenticate=false
-Djava.net.preferIPv4Stack=true
-jar test.jar
命令是一样的,但是一定要记住的是这里的192.168.1.54一定是我们的java的应用地址,而不是jvisual的地址!!
记得开放端口, 然后把需要的添加的机器192.168.1.54添加到jvisualvm的"远程"标签里面,然后在“远程“标签里面添加端口在ip后面就可以。
这里说一下监控远程可能会有一点坑,原因如下:
1)首先确保在java应用的机器里面可以使用jvisualvm才可以,也就是在java应用的机器里面调用jvisualvm没有报错(可能只是warning),才可以。
2)可能是jdk版本的问题。有可能确认了多少遍端口和应用的问题,但是就是连不上,所以就要看看是不是jdk版本的问题(也许不是,所以就在需要监控的java应用程序的机器上先试下jvisualvm)
JVM性能优化建议
一、JVM堆内存划分
JDK7及以前的版本:一个对象被创建以后首先被放到Nursery内存中的Eden内 存中,如果存活期超两个Survivor之后就会被转移到长时内存中(Old Generation)中。永久内存中存放着对象的方法、变量等元数据信息。通过如果永久内存不够,就会得到如下错误:Java.lang.OutOfMemoryError: PermGen
JDK8版本:JDK8中把存放元数据中的永久内存从堆内存中移到了本地内存(native memory)中,这样永久内存就不再占用堆内存,它可以通过自动增长来避免JDK7以及前期版本中常见的永久内存错误(java.lang.OutOfMemoryError: PermGen)。JDK8也提供了一个新的设置Matespace内存大小的参数:
需要注意:如果不设置Matespaces内存大小参数,JVM将会根据一定的策略自动增加本地元内存空间。如果你设置的元内存空间过小,你的应用程序可能得到以下错误:java.lang.OutOfMemoryError: Metadata space
二、JVM参数
-XX 参数被称为不稳定参数,此类参数的设置很容易引起JVM性能上的差异。
1)不稳定参数语法规则
布尔类型
-XX:-<option> '-'表示关闭该选项
数字类型
# 可跟随单位,例如:'m'或'M'表示兆字节;'k'或'K'千字节;'g'或'G'千兆字节。32K与32768是相同大小的。
字符串类型
# 通常用于指定一个文件、路径或一系列命令列表。例如:-XX:HeapDumpPath=./dump.core
2)行为选项
选项 |
默认值 |
描述 |
-XX:-AllowUserSignalHandlers |
限于Linux和Solaris默认关闭 |
允许为java进程安装信号处理器。 |
-XX:AltStackSize=16384 |
仅适用于Solaris,从5.0中删除 |
备用信号堆栈大小(以字节为单位) |
-XX:-DisableExplicitGC |
默认关闭 |
禁止在运行期显式地调用 System.gc()。注意:你熟悉的代码里没调用System.gc(),不代表你依赖的框架工具没在使用。 |
-XX:+FailOverToOldVerifier |
Java6新引入选项,默认启用 |
如果新的Class校验器检查失败,则使用老的校验器。解决兼容性问题。 关联选项:-XX:+UseSplitVerifier |
-XX:+HandlePromotionFailure |
Java1.5以前默认关闭,Java1.6后默认启用 |
新生代收集担保,于年老代预留内存。 |
-XX:+MaxFDLimit |
限于Solaris,默认启用 |
设置java进程可用文件描述符为操作系统允许的最大值。 |
-XX:PreBlockSpin |
默认10 |
控制多线程自旋锁优化的自旋次数 前置选项: -XX:+UseSpinning |
-XX:-RelaxAccessControlCheck |
默认关闭,Java1.6引入 |
在Class校验器中,放松对访问控制的检查。作用与reflection里的setAccessible类似。 |
-XX:+ScavengeBeforeFullGC |
默认启用 |
在Full GC前触发一次Minor GC |
-XX:+UseAltSigs |
限于Solaris 默认启用 |
为了防止与其他发送信号的应用程序冲突,允许使用候补信号替代 SIGUSR1和SIGUSR2。 |
-XX:+UseBoundThreads |
限于Solaris 默认启用 |
绑定所有的用户线程到内核线程。 减少线程进入饥饿状态(得不到任何cpu time)的次数。 |
-XX:-UseConcMarkSweepGC |
默认关闭,Java1.4引入 |
启用CMS低停顿垃圾收集器。 |
-XX:+UseGCOverheadLimit |
默认启用,Java1.6引入 |
限制GC的运行时间。如果GC耗时过长,就抛OutOfMemoryError。 |
-XX:+UseLWPSynchronization |
限于solaris, 默认启用, Java1.4引入 |
使用轻量级进程(内核线程)替换线程同步。 |
-XX:-UseParallelGC |
-server时启用, 其他情况下:默认关闭, Java1.4引入 |
为新生代使用并行清除,年老代使用单线程Mark-Sweep-Compact的垃圾收集器。 |
-XX:-UseParallelOldGC |
默认关闭, Java1.5引入 |
为老年代和新生代都使用并行清除的垃圾收集器。开启此选项将自动开启-XX:+UseParallelGC 选项 |
-XX:-UseSerialGC |
-client时启用, 默认关闭, Java1.5引入 |
使用串行垃圾收集器。 |
-XX:-UseSpinning |
Java1.4.2和1.5需要手动启用, Java1.6默认已启用 |
启用多线程自旋锁优化。关联选项:-XX:PreBlockSpin=10 |
-XX:+UseTLAB |
Java1.4.2以前和使用-client选项时:默认关闭, 其余版本默认启用 |
启用线程本地缓存区(Thread Local) |
-XX:+UseSplitVerifier |
Java1.5默认关闭, Java1.6默认启用 |
使用新的Class类型校验器 。关联选项: -XX:+FailOverToOldVerifier |
-XX:+UseThreadPriorities |
默认启用 |
使用本地线程的优先级。 |
-XX:+UseVMInterruptibleIO |
限于solaris, 默认启用, Java1.6引入 |
在solaris中,允许运行时中断线程 |
3)性能选项
选项 |
默认值 |
描述 |
-XX:+AggressiveOpts |
Java1.5 引入,默认关闭,Java1.6后默认开启 |
开启编译器性能优化。 |
-XX:CompileThreshold=10000 |
默认值:1000 |
通过JIT编译器,将方法编译成机器码的触发阀值,可以理解为调用方法的次数,例如调1000次,将方法编译为机器码。 [-client: 1,500] |
-XX:LargePageSizeInBytes=4m |
默认值:4m,amd64位:2m |
设置堆内存的内存最大值。 |
-XX:MaxHeapFreeRatio=70 |
默认70 |
GC后,如果发现空闲堆内存占到整个预估上限值的70%,则收缩预估上限值。 |
-XX:MaxNewSize=size |
1.3.1 Sparc: 32m,1.3.1 x86: 2.5m |
新生代占整个堆内存的最大值。从Java1.4开始, MaxNewSize成为 NewRatio的一个函数 |
-XX:MaxPermSize=64m |
Java1.5以后::64 bit VMs会增大预设值的30%,1.4 amd64::96m,1.3.1 -client: 32m,其他默认 64m |
Perm(俗称方法区)占整个堆内存的最大值。 |
-XX:MinHeapFreeRatio=40 |
默认值:40 |
GC后,如果发现空闲堆内存占到整个预估上限值的40%,则增大上限值。关联选项:-XX:MaxHeapFreeRatio=70 |
-XX:NewRatio=2 |
Sparc -client: 8,x86 -server: 8 ,x86 -client: 12 -client: 4 (1.3) 8 (1.3.1+) ,x86: 12 ,其他:2 |
新生代和年老代的堆内存占用比例。 例如2表示新生代占年老代的1/2,占整个堆内存的1/3。 |
-XX:NewSize=2m |
5.0以后: 64 bit Vms 会增大预设值的30%,x86: 1m,x86, 5.0以后: 640k,其他:2.125m |
新生代预估上限的默认值。 |
-XX:ReservedCodeCacheSize=32m |
Solaris 64-bit, amd64, -server x86: 48m ,1.5.0_06之前, Solaris 64-bit ,amd64: 1024m ,其他:32m |
设置代码缓存的最大值,编译时用。 |
-XX:SurvivorRatio=8 |
Solaris amd64: 6 ,Sparc in 1.3.1: 25 ,Solaris platforms 5.0以前: 32 ,其他:8 |
Eden与Survivor的占用比例。例如8表示,一个survivor区占用 1/8 的Eden内存,即1/10的新生代内存,为什么不是1/9? 因为我们的新生代有2个survivor,即S1和S22。所以survivor总共是占用新生代内存的 2/10,Eden与新生代的占比则为 8/10。 |
-XX:TargetSurvivorRatio=50 |
默认值:50 |
实际使用的survivor空间大小占比。默认是47%,最高90%。 |
-XX:ThreadStackSize=512 |
Sparc: 512 ,Solaris x86: 320 (5.0以前 256) ,Sparc 64 bit: 1024 Linux amd64: 1024 (5.0 以前 0) ,其他:512. |
线程堆栈大小, |
-XX:+UseBiasedLocking |
Java1.5 update 6后引入,默认关闭。Java1.6默认启用。 |
启用偏向锁 |
-XX:+UseFastAccessorMethods |
默认启用 |
优化原始类型的getter方法性能。 |
-XX:-UseISM |
默认启用 |
启用solaris的ISM |
-XX:+UseLargePages |
Java1.5 update 5后引入,默认关闭,Java1.6默认启用 |
启用大内存分页。关联选项:-XX:LargePageSizeInBytes=4m |
-XX:+UseMPSS |
Java1.4.1 之前默认关闭,其他版本默认启用 |
启用solaris的MPSS,不能与ISM同时使用。 |
-XX:+UseStringCache |
默认开启 |
缓存常用字符串。 |
-XX:AllocatePrefetchLines=1 |
默认值:1 |
在使用JIT生成的预读取指令分配对象后读取的缓存行数。如果上次分配的对象是一个实例则默认值是1,如果是一个数组则是3 |
-XX:AllocatePrefetchStyle=1 |
默认值:1 |
预读取指令的生成代码风格,0- 无预读取指令生成 ,1-在每次分配后执行预读取命令 ,2-当预读取指令执行后使用TLAB()分配水印指针来找回入口, |
-XX:+UseCompressedStrings |
Java1.6 update 21引入 |
其中,对于不需要16位字符的字符串,可以使用byte[] 而非char[]。对于许多应用,这可以节省内存,但速度较慢(5%-10%) |
-XX:+OptimizeStringConcat |
Java1.6 update 20引入 |
在可能的情况下优化字符串连接操作。 |
4)调试选项
选项 |
默认值 |
描述 |
-XX:-CITime |
默认启用 |
打印JIT编译器编译耗时。 |
-XX:ErrorFile=./hs_err_pid.log |
Java1.6引入 |
如果JVM crashed,将错误日志输出到指定文件路径。 |
-XX:-ExtendedDTraceProbes |
Java6引入,限于solaris,默认关闭 |
启用dtrace诊断 |
-XX:HeapDumpPath=./java_pid.hprof |
默认是java进程启动位置 |
堆内存快照的存储文件路径。 |
-XX:-HeapDumpOnOutOfMemoryError |
默认关闭 |
在java.lang.OutOfMemoryError 异常出现时,输出一个dump.core文件,记录当时的堆内存快照(见 -XX:HeapDumpPath 的描述) |
-XX:OnError=”\;\” |
Java1.4引入 |
当java每抛出一个ERROR时,运行指定命令行指令集。指令集是与OS环境相关的,在Linux下多数是.sh脚本,windows下是.bat批处理。 |
-XX:OnOutOfMemoryError=”\;\” |
Java1.4.2 update 12和Java6时引入 |
当第一次发生java.lang.OutOfMemoryError 时,运行指定命令行指令集。指令集是与OS环境相关的,在linux下多数是.sh脚本,windows下是.bat批处理。 |
-XX:-PrintClassHistogram |
默认关闭 |
在Windows下, 按ctrl-break或Linux下是执行kill -3(发送SIGQUIT信号)时,打印class柱状图。 jmap -histo pid也实现了相同的功能。 |
-XX:-PrintConcurrentLocks |
默认关闭 |
在thread dump的同时,打印java.util.concurrent的锁状态。jstack -l pid 也同样实现了同样的功能。 |
-XX:-PrintCommandLineFlags |
Java1.5 引入,默认关闭 |
Java启动时,往stdout打印当前启用的非稳态jvm options。 例如:-XX:+UseConcMarkSweepGC -XX:+HeapDumpOnOutOfMemoryError -XX:+DoEscapeAnalysis |
-XX:-PrintCompilation |
默认关闭 |
往stdout打印方法被JIT编译时的信息。 |
-XX:-PrintGC |
默认关闭 |
开启GC日志打印。显示结果例如: [Full GC 131115K->7482K(1015808K), 0.1633180 secs] ,该选项可通过com.sun.management.HotSpotDiagnosticMXBean API 和 jconsole 动态启用。 |
-XX:-PrintGCDetails |
Java1.4引入,默认关闭 |
打印GC回收的详细信息。 显示结果例如: [Full GC (System) [Tenured: 0K->2394K(466048K), 0.0624140 secs] 30822K->2394K(518464K), [Perm : 10443K->10443K(16384K)], 0.0625410 secs] [Times: user=0.05 sys=0.01, real=0.06 secs] ,该选项可通过com.sun.management.HotSpotDiagnosticMXBean API 和 jconsole 动态启用。 |
-XX:-PrintGCTimeStamps |
默认关闭 |
打印GC停顿耗时。 显示结果例如: 2.744: [Full GC (System) 2.744: [Tenured: 0K->2441K(466048K), 0.0598400 secs] 31754K->2441K(518464K), [Perm : 10717K->10717K(16384K)], 0.0599570 secs] [Times: user=0.06 sys=0.00, real=0.06 secs] ,该选项可通过com.sun.management.HotSpotDiagnosticMXBean API 和 jconsole 动态启用。 |
-XX:-PrintTenuringDistribution |
默认关闭 |
打印对象的存活期限信息。 显示结果例如: [GC Desired survivor size 4653056 bytes, new threshold 32 (max 32) - age 1: 2330640 bytes, 2330640 total - age 2: 9520 bytes, 2340160 total 204009K->21850K(515200K), 0.1563482 secs] ,Age1,2表示在第1和2次GC后存活的对象大小。 |
-XX:-TraceClassLoading |
默认关闭 |
打印class装载信息到stdout。记Loaded状态。 例如: [Loaded java.lang.Object from /opt/taobao/install/jdk1.6.0_07/jre/lib/rt.jar] |
-XX:-TraceClassLoadingPreorder |
1.4.2引入,默认关闭 |
按class的引用/依赖顺序打印类装载信息到stdout。不同于 TraceClassLoading,本选项只记 Loading状态。 例如: [Loading java.lang.Object from /home/confsrv/jdk1.6.0_14/jre/lib/rt.jar] |
-XX:-TraceClassResolution |
1.4.2引入,默认关闭 |
打印所有静态类,常量的代码引用位置。用于debug。 例如: RESOLVE java.util.HashMap java.util.HashMapEntryHashMap.java:209<br>说明HashMap类的209行引用了静态类java.util.HashMapEntryHashMap.java:209<br>说明HashMap类的209行引用了静态类java.util.HashMapEntry |
-XX:-TraceClassUnloading |
默认关闭 |
打印class的卸载信息到stdout。记Unloaded状态。 |
-XX:-TraceLoaderConstraints |
Java1.6 引入,默认关闭 |
打印class的装载策略变化信息到stdout。装载策略变化是实现classloader隔离/名称空间一致性的关键技术。 |
-XX:+PerfSaveDataToFile |
默认启用 |
当java进程因java.lang.OutOfMemoryError 异常或crashed 被强制终止后,生成一个堆快照文件。 |
-XX:ParallelGCThreads=n |
默认值:随JVM运行平台不同而异 |
配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。 |
-XX:+UseCompressedOops |
32位默认关闭,64位默认启动 |
使用compressed pointers。这个参数默认在64bit的环境下默认启动,但是如果JVM的内存达到32G后,这个参数就会默认为不启动,因为32G内存后,压缩就没有多大必要了,要管理那么大的内存指针也需要很大的宽度了 |
-XX:+AlwaysPreTouch |
默认关闭 |
在JVM 初始化时预先对Java堆进行摸底。 |
-XX:AllocatePrefetchDistance=n |
默认值取决于当前JVM 设置 |
为对象分配设置预取距离。 |
-XX:InlineSmallCode=n |
默认值取决于当前JVM 设置 |
当编译的代码小于指定的值时,内联编译的代码。 |
-XX:MaxInlineSize=35 |
默认值:35 |
内联方法的最大字节数。 |
-XX:FreqInlineSize=n |
默认值取决于当前JVM 设置 |
内联频繁执行的方法的最大字节码大小。 |
-XX:LoopUnrollLimit=n |
默认值取决于当前JVM 设置 |
代表节点数目小于给定值时打开循环体。 |
-XX:InitialTenuringThreshold=7 |
默认值:7 |
设置初始的对象在新生代中最大存活次数。 |
-XX:MaxTenuringThreshold=n |
默认值:15,最大值:15 |
设置对象在新生代中最大的存活次数,最大值15,并行回收机制默认为15,CMS默认为4。 |
-Xloggc: |
默认关闭 |
输出GC 详细日志信息至指定文件。 |
-XX:-UseGCLogFileRotation |
默认关闭 |
开启GC 日志文件切分功能,前置选项 -Xloggc |
-XX:NumberOfGClogFiles=1 |
必须>=1,默认值:1 |
设置切分GC 日志文件数量,文件命名格式:.0, .1, …, .n-1 |
-XX:GCLogFileSize=8K |
必须>=8K,默认值:8K |
GC日志文件切分大小。 |
三、垃圾收集器
1. 串行回收器
1.1)新生代串行回收器
特点
- 它仅仅使用单线程进行垃圾回收
- 它是独占式的垃圾回收
- 进行垃圾回收时, Java应用程序中的线程都需要暂停(Stop-The-World)
- 使用复制算法
- 适合CPU等硬件不是很好的场合
设置参数
1.2)老年代串行回收器
特点
- 同新生代串行回收器一样, 单线程, 独占式的垃圾回收器
- 通常老年代垃圾回收比新生代回收要更长时间, 所以可能会使应用程序停顿较长时间
设置参数
-XX:+UseParNewGC 新生代使用ParNew回收器, 老年代使用串行回收器
-XX:+UseParallelGC 新生代使用ParallelGC回收器, 老年代使用串行回收器
2. 并行回收器
2.1)新生代ParNew回收器
特点
- 将串行回收多线程化
- 使用复制算法
- 垃圾回收时, 应用程序仍会暂停, 只不过由于是多线程回收, 在多核CPU上,回收效率会高于串行回收器, 反之在单核CPU, 效率会不如串行回收器
设置参数
-XX:+UseConcMarkSweepGC 新生代使用ParNew回收器, 老年代使用CMS回收器
-XX:ParallelGCThreads=n 指回ParNew回收器工作时的线程数量, cpu核数小时8时, 其值等于cpu数量, 高于8时,可以使用公式(3+((5*CPU_count)/8))
2.2)新生代ParallelGC回收器
特点
- 同ParNew回收器一样, 不同的地方在于,它非常关注系统的吞吐量(通过参数控制)
- 使用复制算法
- 支持自适应的GC调节策略
设置参数
-XX:+UseParallelOldGC 新生代用ParallelGC回收器, 老年代使用ParallelOldGC回收器系统吞吐量的控制:
-XX:MaxGCPauseMillis=n(单位ms) 设置垃圾回收的最大停顿时间,
-XX:GCTimeRatio=n(n在0-100之间) 设置吞吐量的大小, 假设值为n, 那系统将花费不超过1/(n+1)的时间用于垃圾回收
-XX:+UseAdaptiveSizePolicy 打开自适应GC策略, 在这种模式下, 新生代的大小, eden,survivior的比例, 晋升老年代的对象年龄等参数会被自动调整,以达到堆大小, 吞吐量, 停顿时间之间的平衡点
2.3)老年代ParallelOldGC回收器
特点
- 同新生代的ParallelGC回收器一样, 是属于老年代的关注吞吐量的多线程并发回收器
- 使用标记压缩算法
设置参数
-XX:ParallelGCThreads=n 指回ParNew回收器工作时的线程数量, cpu核数小时8时, 其值等于cpu数量, 高于8时, 可以使用公式(3+((5*CPU_count)/8))
3. CMS回收器(Concurrent Mark Sweep,并发标记清除)
3.1)老年代的并发回收器
特点
- 是并发回收, 非独占式的回收器, 大部分时候应用程序不会停止运行
- 针对年老代的回收器
- 使用并发标记清除算法, 因此回收后会有内存碎片, 可以使参数设置进行内存碎片的压缩整理
- 与ParallelGC和ParallelOldGC不同, CMS主要关注系统停顿时间
主要步骤
- 初始标记
- 并发标记
- 预清理
- 重新标记
- 并发清理
- 并发重置
需要注意:初始标记与重新标记是独占系统资源的,不能与用户线程一起执行,而其它阶段则可以与用户线程一起执行
设置参数
-XX:+UseConcMarkSweepGC 老年代使用CMS回收器, 新生代使用ParNew回收器
-XX:ConcGCThreads=n 设置并发线程数量,
-XX:ParallelCMSThreads=n 同上, 设置并发线程数量,
-XX:CMSInitiatingOccupancyFraction=n 指定老年代回收阀值, 即当老年代内存使用率达到这个值时, 会执行一次CMS回收,默认值为68, 设置技巧: (Xmx-Xmn)*(100-CMSInitiatingOccupancyFraction)/100)>=Xmn
-XX:+UseCMSCompactAtFullCollection 开启内存碎片的整理, 即当CMS垃圾回收完成后, 进行一次内存碎片整理, 要注意内存碎片的整理并不是并发进行的, 因此可能会引起程序停顿
-XX:CMSFullGCsBeforeCompation=n 用于指定进行多少次CMS回收后, 再进行一次内存压缩
-XX:+CMSParallelRemarkEnabled 在使用UseParNewGC 的情况下, 尽量减少 mark 的时间
-XX:+UseCMSInitiatingOccupancyOnly 表示只有达到阀值时才进行CMS回收
-XX:+CMSConcurrentMTEnabled 当该标志被启用时,并发的CMS阶段将以多线程执行,默认开启
-XX:+CMSIncrementalMode 在增量模式下,CMS 收集器在并发阶段,不会独占整个周期,而会周期性的暂停,唤醒应用线程。收集器把并发阶段工作,划分为片段,安排在次级(minor) 回收之间运行。这对需要低延迟,运行在少量CPU服务器上的应用很有用
3.2)Class的回收(永久区的回收)
设置参数
-XX:CMSInitiatingPermOccupancyFraction=n 当永久区占用率达到这个n值时,启动CMS回收, 需上一个参数开启的情况下使用
4. G1回收器(JDK1.7后全新的回收器, 用于取代CMS)
4.1)概述
特点
- 独特的垃圾回收策略, 属于分代垃圾回收器
- 使用分区算法, 不要求eden, 年轻代或老年代的空间都连续
- 并行性:回收期间, 可由多个线程同时工作, 有效利用多核cpu资源
- 并发性:与应用程序可交替执行, 部分工作可以和应用程序同时执行
- 分代GC::分代收集器, 同时兼顾年轻代和老年代
- 空间整理:回收过程中, 会进行适当对象移动, 减少空间碎片
- 可预见性:G1可选取部分区域进行回收, 可以缩小回收范围, 减少全局停顿
主要步骤
- 新生代GC
- 并发标记周期
- 混合回收
- 若需要,会进行FullGC
在新生代GC时, survivor区和老年代无法容纳幸存对象时
以上两者都会导致一次FullGC产生
设置参数
-XX:MaxGCPauseMillis=n 指定目标的最大停顿时间,任何一次停顿时间超过这个值, G1就会尝试调整新生代和老年代的比例, 调整堆大小, 调整晋升年龄
-XX:ParallelGCThreads=n 用于设置并行回收时, GC的工作线程数量
-XX:InitiatingHeapOccpancyPercent=n 指定整个堆的使用率达到多少时, 执行一次并发标记周期, 默认45, 过大会导致并发标记周期迟迟不能启动, 增加FullGC的可能, 过小会导致GC频繁, 会导致应用程序性能有所下降
4.2)参数选项
选项 |
默认值 |
描述 |
-XX:+UseG1GC |
默认关闭 |
使用G1垃圾处理器 |
-XX:MaxGCPauseMillis=n |
默认值:4294967295 |
设置并行收集最大暂停时间,这是一个理想目标,JVM将尽最大努力来实现它。 |
-XX:InitiatingHeapOccupancyPercent=n |
默认值:45 |
启动一个并发垃圾收集周期所需要达到的整堆占用比例。这个比例是指整个堆的占用比例而不是某一个代(例如G1),如果这个值是0则代表‘持续做GC’。默认值是45 |
-XX:NewRatio=n |
默认值:2 |
设置年轻代和年老代的比值。例如:值为3,则表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4。 |
-XX:SurvivorRatio=n |
默认值:8 |
年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5 |
-XX:MaxTenuringThreshold=n |
默认值:15 |
设置垃圾最大存活阀值。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。 |
-XX:ParallelGCThreads=n |
默认值:随JVM运行平台不同而异 |
配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。 |
-XX:ConcGCThreads=n |
默认值:随JVM运行平台不同而异 |
并行垃圾收集时,使用的线程数。默认值和JVM运行的平台有关。 |
-XX:G1ReservePercent=n |
默认值:10 |
设置保留用来做假天花板以减少晋升(新生代对象晋升到老生代)失败可能性的堆数目。 |
-XX:G1HeapRegionSize=n |
默认值根据堆大小而定 |
使用G1垃圾回收器,java堆被划分成统一大小的区块。这个选项设置每个区块的大小。最小值是1Mb,最大值是32Mb。 |
4.3)其他GC相关的设置
4.3.1)System.gc()
-XX:+DisableExplicitGC 禁止程序中调用System.gc(), 加了此参数, 程序若有调用, 返回的空函数调用System.gc()的调用, 会使用FullGC的方式回收整个堆而会忽略CMS或G1等相关回收器
2)System.gc()使用并发回收
-XX:+ExplicitGCCinvokesConcurrent 使用并发方式处理显示的gc, 即开启后, System.gc()这种显示GC才会并发的回收, (CMS, G1)
4.3.2)并行GC前额外触发的新生代GC
2)若不需要这种特性, 可以使用以下参数去除
-XX:-ScavengeBeforeFullGC 即去除在FullGC之前的那次新生代GC, 原本默认值为true
4.3.3)对象何时进入老年代
2)当对象年龄达到一定的大小 ,就会离开年轻代, 进入老年代, 对象进入老年代的事件称为晋升, 而对象的年龄是由GC的次数决定的,
每一次GC,若对象没有被回收, 则对象的年龄就会加1, 可以使用以下参数来控制新生代对象的最大年龄:
-XX:MaxTenuringThreshold=n 假设值为n , 则新生代的对象最多经历n次GC, 就能晋升到老年代, 但这个必不是晋升的必要条件
-XX:TargetSurvivorRatio=n 用于设置Survivor区的目标使用率,即当survivor区GC后使用率超过这个值, 就可能会使用较小的年龄作为晋升年龄
3)除年龄外, 对象体积也会影响对象的晋升的, 若对象体积太大, 新生代无法容纳这个对象, 则这个对象可能就会直接晋升至老年代, 可通过以下参
数使用对象直接晋升至老年代的阈值, 单位是byte
-XX:PretenureSizeThreshold 即对象的大小大于此值, 就会绕过新生代, 直接在老年代分配, 此参数只对串行回收器以及ParNew回收有效, 而对ParallelGC回收器无效
4.3.4)在TLAB上分配对象(Thread Local Allocation Buffer, 线程本地分配缓存)
-XX:-UseTLAB 关闭TLAB
-XX:+UseTLAB 开启TLAB, 默认也是开启的
-XX:+PrintTLAB 观察TALB的使用情况
-XX:TLABRefillWasteFraction=n 设置一个比率n, 而refill_waste的值就是(TLAB_SIZE/n), 即TLAB空间较小, 大对象无法分配在TLAB,所以会直接分配到堆上,TLAB较小也很容易装满, 因此当TLAB的空间不够分配一个新对象, 就会考虑废弃当前TLAB空间还是直接分配到堆上, 就会使用此参数进行判断, 小于refill_waste就允许废弃, 而新建TLAB来分配对象,而大于refill_waste就直接在堆上分配, 默认是64
-XX:+ResizeTLAB 开启TLAB自动调整大小, 默认是开启的, 若要关闭把+号换成-号即可
-XX:TLABSize=n 设置一个TLAB的大小, 前提先关闭TLAB的自动调整
5. 性能调优工具
5.1)jps (Java Virtual Machine Process Status Tool)
主要用来输出JVM中运行的进程状态信息。语法格式如下:
# hostid语法如下:
[protocol:][[//]hostname][:port][/servername]
protocol - 如果protocol及hostname都没有指定,那表示的是与当前环境相关的本地协议,如果指定了hostname却没有指定protocol,那么protocol的默认就是rmi。
hostname - 服务器的IP或者名称,没有指定则表示本机。
port - 远程rmi的端口,如果没有指定则默认为1099。
Servername - 注册到RMI注册中心中的jstatd的名称。
# 如果不指定hostid就默认为当前主机或服务器。
# 命令行参数选项
-q 忽略输出类名、Jar名和传入main方法的参数
-m 输出传入main方法的参数
-l 输出main类或Jar的全限名
-v 输出传入JVM的参数
-V 输出通过标记的文件传递给JVM的参数(.hotspotrc文件,或者是通过参数-XX:Flags=<filename>指定的文件)
-J 用于传递jvm选项到由javac调用的java加载器中,例如,“-J-Xms48m”将把启动内存设置为48M,使用-J选项可以非常方便的向基于Java的开发的底层虚拟机应用程序传递参数。
5.2)stack(Java Stack Trace)
主要用来查看某个Java进程内的线程堆栈信息。语法格式如下:
jstack [option] executable core
jstack [option] [server-id@]remote-hostname-or-ip
# 命令行参数选项说明如下:
-l 长列表,会打印出额外的锁信息,在发生死锁时可以用jstack -l pid来观察锁持有情况
-m mixed mode,不仅会输出Java堆栈信息,还会输出C/C++堆栈信息(比如Native方法)
-F 当’jstack [-l] pid’没有相应的时候强制打印栈信息
-h | -help打印帮助信息
jstack可以定位到线程堆栈,根据堆栈信息我们可以定位到具体代码,所以它在JVM性能调优中使用得非常多。
5.3)jmap(Java Memory Map)
jmap用来查看堆内存使用状况,一般结合jhat使用。jmap语法格式如下:(如果运行在64位JVM上,可能需要指定-J-d64命令选项参数)
jmap [option] executable core
jmap [option] [server-id@]remote-hostname-or-ip
# 参数说明
executable 产生core dump的java可执行程序
core 将被打印信息的core dump文件
remote-hostname-or-ip 远程debug服务的主机名或ip
server-id 唯一id,假如一台主机上多个远程debug服务
pid 需要被打印配相信息的java进程id,可以用jps查问
# 基本参数
-dump:[live,]format=b,file=<filename> 使用hprof二进制形式,输出jvm的heap内容到文件=. live子选项是可选的,假如指定live选项,那么只输出活的对象到文件
-finalizerinfo 打印正等候回收的对象的信息
-heap 打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用情况
-histo[:live] 打印每个class的实例数目,内存占用,类全名信息. VM的内部类名字开头会加上前缀”*”. 如果live子参数加上后,只统计活的对象数量
-permstat 打印classload和jvm heap长久层的信息. 包含每个classloader的名字,活泼性,地址,父classloader和加载的class数量. 另外,内部String的数量和占用内存数也会打印出来
-F 强迫.在pid没有相应的时候使用-dump或者-histo参数. 在这个模式下,live子参数无效
-h | -help 打印辅助信息
-J 传递参数给jmap启动的jvm
5.4)jhat(Java Heap Analysis Tool)
jhat用于对JAVA heap进行离线分析的工具,它可以对不同虚拟机中导出的heap信息文件进行分析
jmap -dump:format=b,file=/tmp/dump.dat 21711
jhat -port 9998 /tmp/dump.dat
# 注意如果Dump文件太大,可能需要加上-J-Xmx512m这种参数指定最大堆内存
# 在浏览器中输入主机地址:9998查看
另外,可以使用Eclipse插件MAT(Memory Analyzer Tool)对dump文件进行分析。
5.5)jstat(Java Virtual Machine Statistics Monitoring Tool)
Jstat用于监控基于HotSpot的JVM,对其堆的使用情况进行实时的命令行的统计,使用jstat我们可以对指定的JVM做如下监控:
类的加载及卸载情况
- 查看新生代、老生代及持久代的容量及使用情况
- 查看新生代、老生代及持久代的垃圾收集情况,包括垃圾回收的次数及垃圾回收所占用的时间
- 查看新生代中Eden区及Survior区中容量及分配情况等
语法:
generalOption - 单个的常用的命令行选项,如-help, -options, 或 -version。
outputOptions -一个或多个输出选项,由单个的statOption选项组成,可以和-t, -h, and -J等选项配合使用。
statOption 根据jstat统计的维度不同,可以使用如下表中的选项进行不同维度的统计,不同的操作系统支持的选项可能会不一样,可以通过-options选项,查看不同操作系统所支持选项
-h n 用于指定每隔几行就输出列头,如果不指定,默认是只在第一行出现列头。
-J javaOption 用于将给定的javaOption传给java应用程序加载器,例如,“-J-Xms48m”将把启动内存设置为48M
-t n 用于在输出内容的第一列显示时间戳,这个时间戳代表的时JVM开始启动到现在的时间
vmid VM的进程号,即当前运行的java进程号
interval 间隔时间,单位可以是秒或者毫秒,通过指定s或ms确定,默认单位为毫秒
count 打印次数,如果缺省则打印无数次
===统计维度与输出===
1)class 用于查看类加载情况的统计
2)compiler 用于查看HotSpot中即时编译器编译情况的统计
3)gc 用于查看JVM中堆的垃圾收集情况的统计
4)gccapacity 用于查看新生代、老生代及持久代的存储容量情况
5)gccause 用于查看垃圾收集的统计情况(这个和-gcutil选项一样),如果有发生垃圾收集,它还会显示最后一次及当前正在发生垃圾收集的原因。
6)gcnew 用于查看新生代垃圾收集的情况
7)gcnewcapacity 用于查看新生代的存储容量情况
8)gcold 用于查看老生代及持久代发生GC的情况
9)gcoldcapacity 用于查看老生代的容量
10)gcpermcapacity 用于查看持久代的容量
11)gcutil 用于查看新生代、老生代及持代垃圾收集的情况
12)printcompilation HotSpot编译方法的统计
示例
5.6)jvisualvm(Java Virtual Machine Monitoring, Troubleshooting, and Profiling Tool)
jvisualvm同jconsole都是一个基于图形化界面的、可以查看本地及远程的JAVA GUI监控工具,Jvisualvm同jconsole的使用方式一样,直接在命令行打入Jvisualvm即可启动,不过Jvisualvm相比,界面更美观一些,数据更实时。
5.7)jconsole
一个java GUI监视工具,可以以图表化的形式显示各种数据。并可通过远程连接监视远程的服务器VM。用java写的GUI程序,用来监控VM,并可监控远程的VM,非常易用,而且功能非常强。命令行里打 jconsole,选则进程就可以了。
需要注意:在运行jconsole之前,必须要先设置环境变量DISPLAY,否则会报错误,Linux下设置环境变量如下:
export DISPLAY=:0.0
5.8)jprofile
JProfiler是一个需要商业授权的全功能的Java剖析工具(profiler),专用于分析J2SE和J2EE应用程序。
它把CPU、执行绪和内存的剖析组合在一个强大的应用中。JProfiler可提供许多IDE整合和应用服务器整合用途。JProfiler直觉式的GUI让你可以找到效能瓶颈、抓出内存漏失(memory leaks)、并解决执行绪的问题。它让你得以对heap walker作资源回收器的root analysis,可以轻易找出内存漏失;heap快照(snapshot)模式让未被参照(reference)的对象、稍微被参照的对象、或在终结(finalization)队列的对象都会被移除;整合精灵以便剖析浏览器的Java外挂功能。
5.9)jca
Java线程分析工具,专业的线程分析工具兼容sun/oracle JDK dump线程堆,图形化显示线程概括信息,非常容易的定位问题。
jca是一个类工具 启动方法
5.10)jinfo命令 (Java Configuration Info)
jinfo可以输出并修改运行时的java 进程的opts。用处比较简单,用于输出JAVA系统参数及命令行参数。用法是jinfo -opt pid 如:查看2788的MaxPerm大小可以用 jinfo -flag MaxPermSize 2788。
5.11)Jdb命令 (The Java Debugger)
用来对core文件和正在运行的Java进程进行实时地调试,里面包含了丰富的命令帮助您进行调试。
5.12)Jstatd命令 (Java Statistics Monitoring Daemon)
jstatd是一个基于RMI(Remove Method Invocation)的服务程序,它用于监控基于HotSpot的JVM中资源的创建及销毁,并且提供了一个远程接口允许远程的监控工具连接到本地的JVM执行命令。
jstatd是基于RMI的,所以在运行jstatd的服务器上必须存在RMI注册中心,如果没有通过选项”-p port”指定要连接的端口,jstatd会尝试连接RMI注册中心的默认端口。后面会谈到如何连接到一个默认的RMI内部注册中心,如何禁止默认的RMI内部注册中心的创建,以及如何启动一个外部注册中心。
参数选项
-p port RMI注册中心的端口号,默认为1099。
-n rminame 默认为JStatRemoteHost;如果同一台主机上同时运行了多个jstatd服务,rminame可以用于唯一确定一个jstatd服务;这里需要注意一下,如果开启了这个选项,那么监控客户端远程连接时,必须同时指定hostid及vmid,才可以唯一确定要连接的服务,这个可以参看jps章节中列出远程服务器上Java进程的示例。
-J 用于传递jvm选项到由javac调用的java加载器中,例如,“-J-Xms48m”将把启动内存设置为48M,使用-J选项可以非常方便的向基于Java的开发的底层虚拟机应用程序传递参数。
安全性
jstatd服务只能监视具有适当的本地访问权限的JVM,因此jstatd进程与被监控的JVM必须运行在相同的用户权限中。但是有一些特殊的用户权限,如基于UNIX(TM)为系统的root用户,它有权限访问系统中所有JVM的资源,如果jstatd进程运行在这种权限中,那么它可以监视系统中的所有JVM,但是这也带来了额外的安全问题。
示例
jstatd -J-Djava.security.policy=jstatd.all.policy
# 使用外部的RMI注册中心,启动jstatd
rmiregistry&jstatd -J-Djava.security.policy=all.policy
# 外部的RMI注册中心来启动jstatd,此注册中心的端口为2020
rmiregistry 2020&jstatd -J-Djava.security.policy=all.policy -p 2020
# 外部的RMI注册中心来启动jstatd,此注册中心的端口为2020,并且绑定到RMI注册中心的名为AlternateJstatdServerName
rmiregistry 2020&jstatd -J-Djava.security.policy=all.policy -p 2020 -n AlternateJstatdServerName
# 禁止内部RMI注册中心的创建
jstatd -J-Djava.security.policy=all.policy -nr
# 开启RMI日记记录,jstatd运行在开启了日志记录功能的RMI注册中
jstatd -J-Djava.security.policy=all.policy -J-Djava.rmi.server.logCalls=true
5.13)httpwatch
网页数据分析工具,对客户端到服务器端的请求,响应数据有效的监控分析。
6. 生产环境示例
6.1)Tomcat7
catalina.sh(只运行一个Tomcat)
JAVA_OPTS="
-Dfile.encoding=UTF-8
-server
-Djava.awt.headless=true
-Xms6144m
-Xmx6144m
-XX:NewSize=1024m
-XX:MaxNewSize=2048m
-XX:PermSize=512m
-XX:MaxPermSize=512m
-XX:MaxTenuringThreshold=15
-XX:NewRatio=2
-XX:+AggressiveOpts
-XX:+UseBiasedLocking
-XX:+UseConcMarkSweepGC
-XX:+UseParNewGC
-XX:+CMSParallelRemarkEnabled
-XX:LargePageSizeInBytes=128m
-XX:+UseFastAccessorMethods
-XX:+UseCMSInitiatingOccupancyOnly
-XX:+DisableExplicitGC"
# 16G内存(垃圾回收略)
JAVA_OPTS="
-Dfile.encoding=UTF-8
-server
-Xms13312m
-Xmx13312m
-XX:NewSize=3072m
-XX:MaxNewSize=4096m
-XX:PermSize=512m
-XX:MaxPermSize=512m
-XX:MaxTenuringThreshold=10
-XX:NewRatio=2
-XX:+DisableExplicitGC"
# 32G内存(垃圾回收略)
JAVA_OPTS="
-Dfile.encoding=UTF-8
-server
-Xms29696m
-Xmx29696m
-XX:NewSize=6144m
-XX:MaxNewSize=9216m
-XX:PermSize=1024m
-XX:MaxPermSize=1024m
-XX:MaxTenuringThreshold=10
-XX:NewRatio=2
-XX:+DisableExplicitGC"
6.2)Hadoop
建议Hadoop进程的GC参数加上如下选项,很多商业版都默认加上了,这对JDK性能提升有很大帮助
-XX:+HeapDumpOnOutOfMemoryError
-XX:+UseConcMarkSweepGC
-XX:-CMSConcurrentMTEnabled
-XX:+CMSIncrementalMode
-Djava.net.preferIPv4Stack=true
Could not create the Java virtual machine
1)执行程序里的JAVA路径不对造成的!通过"echo $JAVA_HOME"查找出JAVA路径,然后更新程序中的JAVA路径配置即可。
2)JVM参数不对造成(可能是stack size太小)。修改JVM_OPTIONS配置里的JVM参数值即可。
3)jdk版本不兼容导致。调整jdk版本(比如jdk版本过低,调整为高版本的jdk)即可。
*************** 当你发现自己的才华撑不起野心时,就请安静下来学习吧!***************