Jvm性能调优

目录

一、JVM优化的必要性

二、JVM的运行参数

三、JVM的内存模型

四、jstat模型详解

五、jmap使用详解

六、内存溢出的定位与分析

七、jstack使用及定位死锁问题

八、jvisualvm的使用


一、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)添加成功,如下图所示

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值