java生产环境下性能监控与调优

发布时间 2024-01-11 18:17:11作者: 开源遗迹

JVM的参数类型

基于JDK命令行工具的检测

主要内容

JVM的参数类型

标准参数

-help

-server -client

-version -showversion

-cp -classpath

X参数

非标准化参数

-Xint:解释执行

-Xcomp:第一次使用就编译成本地代码

-Xmixed:混合模式,JVM自己来决定是否编译成本地代码

xx参数

非标准化参数

相对不稳定

主要用于JVM调优和Debug

Boolean类型

格式:-XX:[+-]表示启用或者禁用name属性

比如:-XX:+UseConcMarkSweepGC

​ -XX:+UseG1GC

非Boolean类型

格式:-XX:=表示name属性的值是value

比如:-XX:MaxGCPauseMillis=500

​ XX:GCTimeRatio=19

-Xmx -Xms

不是X参数,而是XX非Boolean类型参数的简写

-Xms等价于-XX:InitialHeapSize

-Xmx等价于-XX:MaxHeapSize

运行时JVM参数查看

查看JVM运行时参数

-XX:+PrintFlagsInitial

-XX:+PrintFlagsFinal

-XX:+UnlockExperimentalVMOptions解锁实验参数

-XX:+UnlockDiagnosticVMOptions解锁诊断参数

-XX:+PrintCommandLineFlags打印命令行参数

image-20240109215620884

=表示默认值

:=被用户或者JVM修改后的值

jps

jinfo

jinfo -flag MaxHeapSize 进程号

jstat查看虚拟机统计信息

类装载

jstat -class 进程号 1000 10

垃圾收集

-gc、-gcutil、-gccause、-gcnew、-gcold

gc输出

S0C、S1C、S0U、S1U:S0和S1的总量与使用量

EC、EU:Eden区总量与使用量

OC、OU:Old区总量与使用量

MC、MU:Metaspace区总量与使用量

CCSC、CCSU:压缩类空间总量与使用量,启用短指针

YGC、YGCT:YoungGC的次数与时间

FGC、FGCT:FUllGC的次数与时间

GCT:总的GC时间

JVM内存结构

S0+S1+EC+EU=Young

OC+OU=Old

Yuong+Old=堆区

Metaspace+CCS+CodeCache=非堆区

JIT编译

-compiler、-printcompilation

jstat -compiler 进程号

输出:编译了多少方法,失败了多少

堆和非堆内存溢出演示

package com.imooc.monitor_tuning.chapter2;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.util.ArrayList;
import java.util.List;

public class Metaspace extends ClassLoader{

    public static List<Class<?>> createClasses(){
        List<Class<?>> classes = new ArrayList<>();
        for (int i = 0; i < 10000000; ++i) {
            ClassWriter cw = new ClassWriter(0);
            cw.visit(Opcodes.V1_1,Opcodes.ACC_PUBLIC,"Class"+i,null,"java/lang/Object",null);
            MethodVisitor mw = cw.visitMethod(Opcodes.ACC_PUBLIC,"<init>","()V",null,null);
            mw.visitVarInsn(Opcodes.ALOAD,0);
            mw.visitMethodInsn(Opcodes.INVOKESPECIAL,"java/lang/Object","<init>","()V");
            mw.visitInsn(Opcodes.RETURN);
            mw.visitMaxs(1,1);
            mw.visitEnd();
            Metaspace test = new Metaspace();
            byte[] code = cw.toByteArray();
            Class<?> exampleClass = test.defineClass("Class"+i,code,0,code.length);
            classes.add(exampleClass);

        }
        return classes;
    }

}

package com.imooc.monitor_tuning.chapter2;


import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@RestController
public class MemoryController {
    private List<User> userList = new ArrayList<>();
    private List<Class<?>> classList = new ArrayList<>();

    //-Xmx32M -Xms32M
    //堆内存溢出
    @GetMapping("/heap")
    public String heap(){
        int i = 0;
        while(true) {
            userList.add(new User(i++, UUID.randomUUID().toString()));
        }
    }


    //-XX:MetaspaceSize=32M -XX:MaxMetaspaceSize=32M
    //非堆,MateSpace溢出
    @GetMapping("/nonheap")
    public String nonheap() {
        int i = 0;
        while (true) {
            classList.addAll(Metaspace.createClasses());
        }
    }


}

导出内存映像文件

如何导出内存映像文件

内存溢出自动导出

-XX:+HeapDumpOnOutOfMemoryError

-XX:HeapDumpPath=./

使用jmap命令手动导出

jps -l

jmap -dump:format=b,file=heap.hprof 进程号

jmap+MAT实战内存溢出

MAT分析内存溢出的映像文件

image-20240110131713287

image-20240110131702436

image-20240110132030950

jstack实战死循环与死锁

jstack与线程状态

jstack 进程号 > 文件名.txt

基于JVisualVM的可视化监控

主要内容

监控本地tomcat

监控远程tomcat

修改tomcat的Catalina.sh

JAVA_OPTS=
"
$JAVA_OPTS -Dcom.sun.management.jmxremote -
Dcom.sun.management.jmxremote.port=9004 -
Dcom.sun.management.jmxremote.authenticate=false -
Dcom.sun.management.jmxremote.ssl=false -
Djava.net.preferIPv4Stack=true -
Djava.rmi.server.hostname=10.110.3.62
"

监控普通的JAVA进程

添加JAVA进程启动参数

nohup java -Dcom.sun.management.jmxremote -
Dcom.sun.management.jmxremote.port=9005 -
Dcom.sun.management.jmxremote.authenticate=false -
Dcom.sun.management.jmxremote.ssl=false -
Djava.net.preferIPv4Stack=true -
Djava.rmi.server.hostname=10.110.3.62 -jar monitor_tuning.jar &

使用JVisualVM远程连接JMX线程

基于Btrace的监控调优

Btrace简介

Btrace可以动态地向目标应用程序的字节码注入追踪代码

JavaComplierApi、JVMTI、Agent、Instrumentation+ASM

Btrace安装

新建环境变量BTRACE_HOME

添加Path:%BTRACE_HOME%\bin

例:

@RestController
@RequestMapping("/ch4")
public class Ch4Controller {

    @RequestMapping("arg1")
    public String arg1(@RequestParam("name")String name) {
        return "hello,"+name;
    }
}
@BTrace
public class PrintArgSimple {

    @OnMethod(
            clazz = "com.imooc.monitor_tuning.chapter4.Ch4Controller",
            method = "arg1",
            location = @Location(Kind.ENTRY)
    )
    public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, AnyType[] args){
        BTraceUtils.printArray(args);
        BTraceUtils.println(pcn+","+pmn);
        BTraceUtils.println();
    }
}

btrace 20092 PrintArgSimple.java

拦截构造函数、同名函数

使用详解

拦截方法

拦截时机

拦截this、参数、返回值

其他

普通方法@OnMethod(clazz="",method="")

构造函数@OnMethod(clazz="",method="")

@BTrace
public class PrintArgSimple {

    @OnMethod(
            clazz = "com.imooc.monitor_tuning.chapter2.User",
            method = "<init>"
      
    )
    public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, AnyType[] args){
        BTraceUtils.printArray(args);
        BTraceUtils.println(pcn+","+pmn);
        BTraceUtils.println();
    }
}

拦截同名函数,用参数区分

@BTrace
public class PrintArgSimple {

    @OnMethod(
            clazz = "com.imooc.monitor_tuning.chapter4.Ch4Controller",
            method = "same"
      
    )

    public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, String name,int id){
        BTraceUtils.println(pcn+","+pmn+","+name+","+id);
        BTraceUtils.println();
    }
    
    public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, String name){
        BTraceUtils.println(pcn+","+pmn+","+name);
        BTraceUtils.println();
    }
}

拦截时机

Kind.ENTRY:入口,默认值

Kind.RETURN:返回

Kind.THROW:异常

Kind.Line:行

拦截返回值、异常、行号

返回值

    @OnMethod(
            clazz = "com.imooc.monitor_tuning.chapter4.Ch4Controller",
            method = "arg1"
        	location = @Location(Kind.RETURN)
      
    )

 public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, @Return AnyType result)	{
        BTraceUtils.println(pcn+","+pmn+","+result);
        BTraceUtils.println();
    }

异常

@TLS
static Throwable currentException;

@OnMethod(
            clazz = "java.lang.Throwable",
            method = "<init>"
    )
public static void onthrow(@Self Throwable self){
    currentException = self;
}
@OnMethod(
            clazz = "java.lang.Throwable",
            method = "<init>"
    )
public static void onthrow1(@Self Throwable self,String s,Throwable cause){
    currentException = self;
}
@OnMethod(
            clazz = "java.lang.Throwable",
            method = "<init>"
    )
public static void onthrow2(@Self Throwable self,Throwable cause){
    currentException = self;
}
@OnMethod(
            clazz = "java.lang.Throwable",
            method = "<init>",
    		location = @Location(Kind.RETURN)
    )
public static void onthrowreturn(){
    if(currentException != null) {
        BTraceUtils.Threads.jstack(currentException);
        BTraceUtils.println("========");
        currentException = null;
    }
     
}

行号

    @OnMethod(
            clazz = "com.imooc.monitor_tuning.chapter4.Ch4Controller",
        	location = @Location(value=Kind.Line,line=35)
      
    )

 public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn,int line)	{
        BTraceUtils.println(pcn+","+pmn+","+line);
        BTraceUtils.println();
    }

拦截复杂参数、坏境变量、正则匹配拦截

this:@self

入参:可以用AnyType,也可以用真是类型,同名的用真实的

返回:@Return

简单类型:直接获取

复杂类型:反射,类名+属性名

    @OnMethod(
            clazz = "com.imooc.monitor_tuning.chapter4.Ch4Controller",
            method="arg2",
        	location = @Location(value=Kind.ENTRY)
      
    )

 public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn,User user)	{
        BTraceUtils.printFiles(user);
        Field filed2 = BTraceUtils.field("com.imooc.monitor_tuning.chapter2.User","name")
        BTraceUtils.println(BTraceUtils.get(filed2, user));
        BTraceUtils.println(pcn+","+pmn);
        BTraceUtils.println();
    }

打印行号:Kind.LINE

打印堆栈:Threads.jstack()

打印环境变量

注意事项

默认只能在本地运行

生产环境下可以使用,但是被修改的字节码不会被还原

主要内容

tomcat远程debug

jdwp

修改bin/startup.sh

exec "$PRGDIR"/"$EXECUTABLE" jpda start "$@"

修改bin/catalina.sh的端口或默认使用8000

使用eclipse或idea连接

tomcat-manager监控

低版本开启,高版本默认不开启

步骤conf/tomcat-users.xml添加用户

<role rolename="tomcat"/>
<role rolename="manager-status"/>
<role rolename="manager-gui"/>
<user username="tomcat" password="123456" roles="tomcat,manager-gui,manager-status"/>

conf/Catalina/localhost/manager.xml配置允许的远程连接

<?xml version="1.0" encoding="UTF-8"?>
<Context privileged="true" antiResourceLocking="false"
doBase="${catalina.home}/webapps/manager">
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127\.0\.0\.1"/>
</Context>

重启,访问http://127.0.10.1:8080/manager

psi-probe监控

下载地址https://github.com/psi-probe/psi-probe

在目录下mvn clean package -Dmaven.test.skip

将war包放到tomcat的webapps下

同样要修改conf/Catalina/localhost/manager.xml和conf/tomcat-users.xml添加用户

tomcat调优

内存优化

线程优化

配置优化

Nginx性能监控与调优

ngx_http_stub_status监控连接信息

ngxtop监控请求信息

按赚python-pip

yum install epel-release

yum install python-pip

安装ngxtop

pip install ngxtop

nginx-rrd图形化监控

需要安装php

JVM字节码与Java代码层调优