目录
一、JVM优化的必要性
本地环境和生产环境大相径庭
(1) 运行的应用“卡住了”,日志不输出,程序没有反应
(2) 服务器的CPU负载突然升高
(3) 多线程应用下,如何分配线程的数量?
二、JVM的运行参数
1 三种参数类型
(1) 标准参数
-help
-version
(2) -X参数(非标准参数)
-Xint
-Xcomp
(3) -XX参数(使用率较高)
-XX:newSize
-XX:+UserSerialGC
2 标准参数
(1)标准参数一般都很稳定,可以使用java -help查看
(2)实战
a.查看JVM版本 java -version
b.通过-d设置系统属性参数
测试-d参数,创建Test.JVM.java文件
package com.tydic.common;
/**
* TODO
*
* @author Andy
* @date 2021/3/28 12:10
*/
public class TestJVM {
public static void main(String[] args){
String name = System.getProperty("name");
if(name !=null){
System.out.println(name);
}else{
System.out.println("Andy");
}
}
}
编译,运行结果如下:
加上参数,重新执行
(3)实战意义
通过系统参数可以传入项目环境标记,根据不同环境选择不同配置
(4)通过-server或-client设置JVM的运行参数
server模式下初始化的堆空间可能会大一点,分配的内存会更多一点,它默认使用的是并行垃圾回收器,启动的时候慢,运行的时候快
client模式初始化堆空间可能会小一点,因为它使用的是串行垃圾回收器
32位操作系统:如果是windows系统,不论硬件配置如何,都默认使用Client类型的JVM;如果是其他操作系统,机器配置有2GB以上的内存空间有2个以上CPU的话默认使用server模式,否则使用client模式
64位操作系统:只支持server类型,不支持client类型
3 非标准参数
(1)-X参数
-X参数是非标准参数,在不同版本的JVM中。参数可能会有所不同,可以通过Java -X查看非标准参数
-Xint在解释模式(interpreted mode)下,-Xintinterpreted mode)下,-Xint标记会强制JVM执行所有的字节码,当然这回降低运行速度,通常低10倍或更多
-Xcomp参数与它(-Xint )正好相反,JVM在第一次使用时会把所有的字节码编译成本地代码,从而代码最大程度的优化
-Xmixed是混合模式,将解释模式和编译模式混合使用,由JVM自己决定,这是jvm的默认的模式,也是推荐使用的模式
用编译模式执行如下
如果什么都不加,默认就是混合模式
(2)-XX参数
a.-XX参数也是非标准参数,主要用于jvm的调优和debug操作
b.-XX参数的使用用2种方式,一种是boolean类型,一种是非Boolean类型
boolean类型
非boolean类型
(3)-Xms与-Xmx参数
a.-Xms与-Xmx分别是设置jvm的堆内存的初始大小和最大大小
b.-Xmx2048m:等价于-XX:MaxHeapSize,设置JVM最大堆内存为2048M
c.-Xms512m:等价于-XX:InitialHeapSize,设置JVM初始堆内存为512M
(4)查看JVM的运行参数
a.运行Java命令时打印参数:需要添加-XX:+PrintFlagsFinal参数即可
b.查看正在运行的jvm参数,需要借助于jinfo命令查看
查单个参数:
三、JVM的内存模型
openjdkjdk已经到16了,但是jdk8的综合性能依然名列前茅,线上环境使用双8仍是主流(tomcat8.5,jdk1.8)
1、JDK1.7的堆内存模型
最主要的几块:新生代,老年代,永久代(保存class信息,字节码信息等)
其中伊甸区是新生代里面使用最频繁的区域,new出来的对象都是先经过伊甸区,经过一段时间的运行,伊甸区的对象会逐渐增多,垃圾处理完成还未被回收的对象,会被转移到“幸存区”。其中,幸存区包含两块(S0,S1),其中一块满了之后会向另一块转移。如果幸存区的对象经过GC回收以后还存在,则会进入老年带,老年带的对象一般是很难被回收的,这里面的对象一般是确实一直被使用,或者说是程序写的有问题。一般fullGC处理的对象就是老年带中的对象。
2、JDK1.8的堆内存模型
由上图可知,1.8和1.7最明显的区别堆内存里面只包含新生带和老年带,少了永久带,多了元数据空间MetaData
3、为什么要废弃1.7中的永久区
四、jstat模型详解
通过jstat命令查看堆内存使用情况
1 查看class加载统计
其中Loadedoaded是加载的class数量,Bytes是加载文件的大小K,Unloaded是未加载的文件数量,Bytes是未加载占用的空间,Timeime指时间(毫秒)
2 查看编译统计
3 查看垃圾回收统计
每隔一秒共打印5次
五、jmap使用详解
[root@iZ8vbcm2dqz5w7dpnsrqk8Z ~]# jmap -heap 11296
Attaching to process ID 11296, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.211-b12
using thread-local object allocation.
Parallel GC with 8 thread(s)
Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 16580083712 (15812.0MB)
NewSize = 346030080 (330.0MB)
MaxNewSize = 5526519808 (5270.5MB)
OldSize = 692060160 (660.0MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
PS Young Generation
Eden Space:
capacity = 107479040 (102.5MB)
used = 98547128 (93.98186492919922MB)
free = 8931912 (8.518135070800781MB)
91.68962432116997% used
From Space:
capacity = 4718592 (4.5MB)
used = 4112400 (3.9218902587890625MB)
free = 606192 (0.5781097412109375MB)
87.15311686197917% used
To Space:
capacity = 4718592 (4.5MB)
used = 0 (0.0MB)
free = 4718592 (4.5MB)
0.0% used
PS Old Generation
capacity = 1473249280 (1405.0MB)
used = 984995112 (939.3645401000977MB)
free = 488254168 (465.63545989990234MB)
66.85868612812082% used
79537 interned Strings occupying 8745616 bytes.
(1)查看内存中所有对象,包括活跃以及非活跃的
jmap -histo <pid> | more
(2)查看活跃数量
jmap -histo:live <pid> | more
(3)将内存使用情况dump到文件中
a.有些时候我们需要将JVM当前内存中的情况dump到文件中,然后对它进行分析,jmap也支持dump到文件中
#用法:
jmap -dump:format=b,file=dumpFileName <pid>
#示例
jmap -dump:format=b,file=/temp/dump.dat 11296
(4)使用jhat工具对dump文件进行分析
通过dump命令出来的文件是二进制文件,不方便查看,这时我们可以借助jhat工具进行查看
jhat -port 8888 11296
(5)通过MAT工具对dump文件进行分析
MAT工具介绍
MAP(Memory Analyzer Tool),一个基于Eclipse的内存分析工具,是一个快速,功能丰富的JAVA heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗。使用内存分析工具从众多的对象中进行分析,快速的计算出在内存中对象的占用大小,看看是谁阻止了垃圾收集器的回收工作,并可以通过报表直观的查看到可能造成这种结果的对象。
官网地址:https://www.eclipse.org/mat
六、内存溢出的定位与分析
1、模拟内存溢出
(1)编写代码,向List集合中添加100万个字符串,每个字符串由1000个UUID组成。如果程序能够正常执行,最后打印ok(idea编辑器中需要设置内存溢出相关参数)
package com.andy.vm;
import java.util.ArrayList;
import java.util.UUID;
/**
* TODO
*
* @author Andy
* @date 2021/4/4 9:02
*/
public class TestOOM {
public static void main(String[] args) {
ArrayList<String> stringArrayList = new ArrayList<>();
for(int i=0;i<1000000;i++){
String str = "";
for(int j=0;j<1000;j++){
str= str+ UUID.randomUUID().toString();
stringArrayList.add(str);
}
}
System.out.println("it is over!");
}
}
打开MAT工具,找到文件并打开,默认选第一个,内存泄漏分析Leak suspects
打开之后可以看出MAT已经帮助检测了可能出现的问题“本地变量占用内存过高”
通过dominator_tree视图也可以看出主要是ArrayList中的Object对象占据了大量的内存
七、jstack使用及定位死锁问题
1、线程的生命周期
2、模拟死锁
相关命令”
jps:查找运行程序pid
jstack pid:查看线程栈
package com.zw.rule.service.util;
public class DeadLock {
private static Object obj1 = new Object();
private static Object obj2 = new Object();
public static void main(String[] args) {
new Thread(new Thread1()).start();
new Thread(new Thread2()).start();
}
private static class Thread1 implements Runnable{
@Override
public void run() {
synchronized (obj1){
try {
System.out.println(">>>>>>>>>>>>>>>>"+Thread.currentThread().getName()+"获取了obj1锁");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj2){
System.out.println(">>>>>>>>>>>>>>>>"+Thread.currentThread().getName()+"获取了obj2锁");
}
}
}
}
private static class Thread2 implements Runnable{
@Override
public void run() {
synchronized (obj2){
try {
System.out.println(">>>>>>>>>>>>>>>>"+Thread.currentThread().getName()+"获取了obj2锁");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj1){
System.out.println(">>>>>>>>>>>>>>>>"+Thread.currentThread().getName()+"获取了obj1锁");
}
}
}
}
}
上述代码如果不主动停止会一直处于僵持状态,线程1需要获取obj2锁,线程2想获取obj1锁
拷贝代码到服务器上运行
查看DeadLock.java的进程号
执行jstack
从打印的堆栈可以看出发生了死锁
八、jvisualvm的使用
使用jvisualvm能够监控线程,内存情况,查看方法的CPU时间和内存中的对象,已被GC的对象,反向查看分配的堆栈(如100个String对象分别由哪几个对象分配出来的)。jvisualvm使用简单,几乎0配置,功能还是比较丰富的,几乎囊括了其它JDK自带命令的所有功能
1、启动
在jdk安装目录的bin目录下,找到jvisualvm.exe即可启动,双击打开即可
2、什么是JMX?
JMX(Java Management Extensions,即Java管理扩展),是一个为应用程序,设备,系统等植入管理功能的框架。JMX可以跨越一系列异构操作系统平台,系统体系结构和网络传输协议,灵活的开发无缝集成的系统,网络和服务管理应用
3、监控远程的Tomcat
需要监控远程的Tomcat,就需要再远程的Tomcat进行对JMX的配置,配置完毕重启Tomcat
4、使用jvisualvm连接远程的Tomcat
(1)添加远程主机
(2)在一个主机下可能有很多的JVM需要监控,所以在该主机上添加需要监控的JVM
(3)添加成功,如下图所示