一、jvm基本概况
1、必要性
在生产环境中可能会出现应用无反应,CPU飙升,内存占用太大等问题,可以从jvm的角度考虑。
2、jvm参数
2.1 jvm参数分为三种:
- 标准参数
-help
-version - -X参数(非标准参数)
-Xint
-Xcomp - -XX参数(使用率较高)
-XX:newSize
-XX:UseSerialGC
2.2 标准参数
jvm的标准参数一般都很稳定,在未来的jvm版本中不会改变,可以使用java -help查看所有标准参数。
如:-help,-version,-D<名称>=<值>
2.3 非标准参数
jvm的-X参数是非标准参数,在不同的jvm版本中,参数可能会不同,可以使用java -X查看非标准参数。
如:-Xint,-Xcomp,-Xmixed
2.4 -XX参数
-XX参数也是非标准参数,主要用于jvm调优和dubug操作。
-XX参数的使用有两种类型,一种是boolean类型,一种是非boolean类型。
- boolean模式
-XX:[±]< name >表示启用或禁用name属性。
如-XX:+DisableExplicitGC,表示禁止手动调用gc操作,也就是说System.gc()无效。 - 非boolean模式
-XX:< name >=< value > 表示name属性的值为value
如:-XX:NewRatio=1 表示新生代和老年代的比值
2.5 -Xms和-Xmx参数
-Xms和-Xmx分别是失职jvm堆内存初始值和最大值。
-Xmx2048m:等价于-XX:MaxHeapSize,设置JVM最大堆内存为2048M。
-Xms512m:等价于-XX:InitialHeapSize,设置JVM初始堆内存为512M。
2.6 查看jvm运行参数 - 运行java命令时打印出运行参数
只需要添加-XX:+PrintFlagsFinal参数即可。
如java -XX:+PrintFlagsFinal -version
由上述的信息可以看出,参数有boolean类型和数字类型,值的操作符是=或:=,分别代表默认值和被修改的值。 - 查看正在运行的java进程的参数
这种情况下需要借助jinfo命令查看。
查看所有参数方法:jinfo -flags <进程id>
查看某一个参数:jinfo -flag <参数名> <进程id>
3、jvm内存模型
jvm内存模型在1.7和1.8版本之间有较大变化。
3.1 jdk1.7内存模型
- Young 年轻代
年轻代被划分成三个区,Eden和两个大小严格相同的Survivor区,Survivor区,同一时刻只有一个区被使用,另一个用于垃圾收集时复制对象使用。当Eden区变满时,GC就会把Eden中存活的对象复制到Survivor区,根据JVM的策略,在经过几次垃圾回收之后依然存活的对象会被转移到Tenured区。 - Tenured 老年代
主要用于保存生命周期较长的对象,当年轻代对象在多次垃圾回收之后依然存活就会被转移到Tenured区,一些大对象也会直接被分配在Tenured区。 - Perm 永久代
主要用于保存class,method,filed对象。这部份的空间一般不会溢出,除非一次性加载了很多的类。或者热部署时重新部署后,类的class没有被卸载掉,造成了大量的class对象保存在了perm中。 - Virtual区
最大内存和初始内存的差值。
3.2 jdk1.8内存模型
年轻代:Eden + 2*Survivor
年老代:OldGen
在1.8中Perm区取消,使用Metaspace(元数据空间)进行替换。
Metaspace使用的内存空间不是虚拟机内部,不属于堆内存,而是本地内存空间,这是与1.7最大的区别所在。
3.3 为什么要弃用永久区
官网给出了解释:http://openjdk.java.net/jeps/122
This is part of the JRockit and Hotspot convergence effort. JRockit customers do not need to configure the permanent generation (since JRockit does not have a permanent generation) and are accustomed to not configuring the permanent generation. 移除永久代是为融合HotSpot JVM与 JRockit VM而做出的努力,因为JRockit没有永久代,不需要配置永久代。
3.4 jstat命令查看堆内存
jstat可以查看堆内存各部分使用量以及类加载情况。
jstat [-命令选项] [vmid] [间隔时间/毫秒] [查询次数] - jstat -class < pid >
类加载统计
Loaded:加载class的数量
Bytes:所占用空间大小
Unloaded:未加载数量
Bytes:未加载占用空间
Time:时间 - jstat -compiler < pid >
编译统计
Compiled:编译数量。
Failed:失败数量
Invalid:不可用数量
Time:时间
FailedType:失败类型
FailedMethod:失败的方法 - jstat -gc < pid >
指定打印的间隔和次数,每1秒中打印一次,共打印5次
S0C:第一个Survivor区的大小(KB)
S1C:第二个Survivor区的大小(KB)
S0U:第一个Survivor区的使用大小(KB)
S1U:第二个Survivor区的使用大小(KB)
EC:Eden区的大小(KB)
EU:Eden区的使用大小(KB)
OC:Old区大小(KB)
OU:Old使用大小(KB)
MC:方法区大小(KB)
MU:方法区使用大小(KB) CCSC:压缩类空间大小(KB) CCSU:压缩类空间使用大小(KB) YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
4、jmap使用及内存溢出分析
4.1 查看内存使用情况
jmao -heap < pid >
4.2 查看内存中数量和大小
查看所有对象,包括活跃和非活跃的
jmap -histo < pid > | more
查看活跃对象
jmap -histo:live < pid > | more
4.3 将内存使用情况dump到文件中
jmap -dump:format=b,file=dumpFileName < pid >
4.4 通过jhat对dump文件分析
使用jmap对内存dump的文件是二进制文件,可以借助jhat工具进行查看。
jhat -port < port > < file >
可以使用浏览器访问9999端口即可。
4.5 使用mat工具对dump文件进行分析
MAT(Memory Analyzer Tool),是一个基于Eclipse的内存分析工具,可以查找内存泄漏,分析内存对象,提供报表分析展示。这款工具不仅可以分析dump出来的内存文件还可以直接连接运行的服务器获取内存文件。
官网地址:https://www.eclipse.org/mat/
5. jstack的使用
jstack主要用于查看jvm线程情况。jstack可以将正在运行的jvm线程情况进行快照。
jstack < pid>
可以看到,程序中出现了一个死锁。
5.1 线程的状态
- 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
- 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。 - 阻塞(BLOCKED)。
当一条正在执行的线程请求某一资源失败时,就会进入阻塞态。
而在Java中,阻塞态专指请求锁失败时进入的状态。 - 等待(WAITING)
当前线程中调用wait、join、park函数时,当前线程就会进入等待态。
线程处于等待态表示它需要等待其他线程的指示才能继续运行。
进入等待态的线程会释放CPU执行权,并释放资源(如:锁) - 超时等待(TIMED_WAITING)
它和等待态一样,并不是因为请求不到资源,而是主动进入,并且进入后需要其他线程唤醒;
进入该状态后释放CPU执行权 和 占有的资源。
与等待态的区别:到了超时时间后自动进入阻塞队列,开始竞争锁。 - 终止(TERMINATED)
表示该线程已经执行完毕。
6、VisualVM工具的使用
VisualVM能够监视线程、内存情况。在jdk的bin目录下。
内存信息
线程信息
Dump堆(本地进程)
Dump线程(本地进程)
打开堆Dump。堆Dump可以用jmap来生成。
打开线程Dump
生成应用快照(包含内存信息、线程信息等等)
性能分析。CPU分析(各个方法调用时间,检查哪些方法耗时多),内存分析(各类对象占用的内存,检查哪些类占用内存多)
……
6.1监视远程的jvm
JMX(Java Management Extensions,即Java管理扩展)是一个为应用程序、设备、系统等植入管理功能的框架。JMX可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用。
如果要监视远程的tomcat,需要对远程tomcat进行JMX配置。
#在tomcat的bin目录下,修改catalina.sh,添加如下的参数 JAVA_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9999 - Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false"
#这几个参数的意思是:
#-Dcom.sun.management.jmxremote :允许使用JMX远程管理
#-Dcom.sun.management.jmxremote.port=9999 :JMX远程连接端口
#-Dcom.sun.management.jmxremote.authenticate=false :不进行身份认证,任何用户都可以连接
#-Dcom.sun.management.jmxremote.ssl=false :不使用ssl