欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

一文搞定JVM调优

程序员文章站 2022-07-13 09:07:43
...

JVM内存模型

一文搞定JVM调优

  • 程序计数器:线程私有,记录当前线程字节码执行的位置,占用内存很小,java虚拟机规范中唯一没有规定OOM的区域。
  • java虚拟机栈:线程私有,每个java方法调用时候创建一个栈帧入栈,结束后出栈,所有栈帧出栈,线程结束。
    • 局部变量表:存放方法参数与内部定义的局部变量,编译期就确定最大容量。
    • 操作数栈:方法执行就是各种字节码指令向操作数栈中写入和读取数据,也就是入栈和出栈,编译期就确定栈的最大深度。
    • 动态链接:保存指向运行时常量池中该栈帧所属方法的引用,运行时期间将符合引用转化为直接引用,找到对应的类和方法。在编译期间就能转换为直接引用的叫做静态解析。此处不明白可以先往下看。
    • 方法出口:方法执行完后返回的位置,一般可以是上一个栈帧的计数,如果方法异常,返回位置通过异常处理器来定位。
  • 本地方法栈:同java虚拟机栈类似,执行的是native方法,非java实现,一般由C/C++实现。
    • new出来的对象放在堆中,栈中的局部变量引用指向此处
    • 字符串常量池:存放String字符串的实例
    • 运行时常量池:每个class独有,当类加载到内存中,jvm就会将class常量池中的内容放到运行时常量池中,类在解析之后,将符号引用替换成直接引用。运行时常量区中的字符串常量和字符串常量池保持一致。。
  • 元空间:元空间存储class的信息,堆中的每个对象都会有一个指向元空间中具体实现类的指针。
    • 类描述信息:类的版本、字段、方法、接口等描述信息
    • class常量池:存放编译器生成的各种字面量(常量)和符号引用(一组符号来描述所引用的目标,一般包括类和接口的全限定名、字段的名称和描述符以及方法的名称和描述符,比如People中引用的Name,编译期间不知道Name的直接地址,所以存放的是符号引用,在运行时常量区中会转换成直接引用)
  • 直接内存:不是JVM定义的内存区域,堆外内存。

常用JVM参数

  • -Xms :程序启动时占用内存大小,初始堆大小,一般Xms和Xmx设置相同,避免gc后调整堆大小。

  • -Xmx :程序运行期间最大可占用的内存大小,最大堆大小,超出会抛出OutOfMemory异常。

  • -Xmn :设置年轻代大小,注意,增大年轻代会减少老年代大小,官方推荐为堆的3/8

  • -Xss :设置每个线程的堆栈大小,jdk1.5后为1M

  • -XX:+HeapDumpOnOutOfMemoryError :JVM在发生内存溢出时自动的生成堆内存快照

  • -XX:HeapDumpPath= : 堆内存快照会保存在JVM的启动目录下名为java_pid.hprof 的文件里(在这里就是JVM进程的进程号),可以通过该命令指定

  • -XX:OnOutOfMemoryError :内存溢出时,可以指定运行脚本等。例如-XX:OnOutOfMemoryError =”sh cleanup.sh”

  • -XX:PermSize : 永久代初始大小,jdk1.8后已经移除永久代

  • -XX:MaxPermSize :最大永久代大小,jdk1.8后已经移除永久代

  • -XX:MetaspaceSize :首次使用不够而触发FGC的阈值,达到会进行fullgc,后会计算新的FGC值,和此项无关,默认为20M左右。

  • XX:MaxMetaspaceSize :用于设置metaspace区域的最大值,元空间不使用jvm内存,使用的是本地内存,不设置默认-1无穷大,会根据本地内存大小相关,超过会OOM

  • -XX:+PrintGCDetails:打印GC详细的日志信息

  • -XX:+PrintCommandLineFlags:JVM打印出那些已经被用户或者JVM设置过的详细的XX参数的名称和值

  • -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=1506:开启远程调试

    -XDebug:表示虚拟机启用调试功能

    -Xrunjdwp:加载JDWP

    transport:调试程序JVM使用的进程之间通讯方式

    dt_socket:socket通讯

    server=y/n:JVM是否需要作为调试服务器执行

    address:调试服务器监听的端口号

    suspend=y/n:调试客户端建立连接之后启动虚拟机

  • -XX:+UseSerialGC:虚拟机运行在Client模式下的默认值,Serial+Serial Old

  • -XX:+UseParNewGC:ParNew+Serial Old,在JDK1.8被废弃,在JDK1.7还可以使用

  • XX:+UseConcMarkSweepGC:ParNew+CMS+Serial Old

  • -XX:+UseParallelGC:虚拟机运行在Server模式下的默认值,Parallel Scavenge+Serial Old(PS Mark Sweep)

  • -XX:+UseParallelOldGC:Parallel Scavenge+Parallel Old,常用

  • -XX:+UseG1GC:G1+G1,jdk1.8后可用

常用JVM命令

  • jps

    • jps -q:只显示当前进程的pid,后续命令需要使用

    • jps -v:可以显示jvm参数

    • jps -l :输出完全的包名,应用主类名,jar的完全路径名

  • jinfo

    • jinfo :可以查看JVM的运行参数
    • jinfo -flag name :查看对应参数的值
    • jinfo -flag [+|-]name :开启(+)/关闭(-)对应名称的参数
    • jinfo -flag name=value : 修改对应名称参数的值
  • jstat

    • jstat -class :查看类加载数量和占用内存,未加载数量和占用内存,加载时长
    • jstat -compiler : 查看类编译数量,失败数量,不可用数量,编译时长,失败类型和方法
    • jstat -gc : 垃圾回收统计,每个分代的大小和已使用大小等
    • jstat -gccapacity : 堆内存统计,可以查看分代的容量和gc的次数等
    • jstat -gcnew : 年轻代内存情况以及ygc次数和消耗总时间等
    • jstat -gcold : 老年代内存情况以及fgc次数和消耗时间等
    • jstat -gcutil : 垃圾回收统计,分代内存使用比例
  • jstack

    • jstack : 查看进程中线程的堆栈信息,查看线程的状态,可以定位死锁,每个线程都有tid(线程编号)和nid(线程id)
    • top -Hp : 查看进程内所有线程的CPU和内存使用情况,找到对应的CPU内存最高的通过printf “%x%n” 转换为16进制
    • jstack | grep <16进制> -A 10:打印对应线程的堆栈信息
  • jmap

    • jmap -dump:[live],format=b,file=myjmapfile.txt : [live]可选参数,如果就用live,则输出活的对象到内存文件,慎用,会暂停应用
    • jmap -finalizerinfo : 打印正在回收对象的数量和信息
    • jmap -heap : 查看GC使用的算法,堆的配置及JVM堆内存的使用情况,慎用,可能导致进程挂起
    • jmap -histo:live : 查询每个class的实例数目和内存占用,会先触发gc再统计
    • jmap -permstat : 打印所有classLoad以及class的加载数和内存,慎用,暂停应用

dump快照分析

场景:java.lang.OutOfMemoryError: Java heap space错误

原因:在JVM中如果98%的时间是用于GC且可用的 Heap size 不足2%的时候将抛出此异常信息。

实例:写一个无限循环并设置jvm参数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
JVM参数: -Xms1m -Xmx1m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails

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

public class Test {
    public static void main(String[] args) {
        List<Test> list = new ArrayList<Test>();
        while(true) {
            list.add("test");
        }
    }
}

首先我们可以看到GC日志,可以看到youngGC与fullGc触发前后的内存变化和执行时间

一文搞定JVM调优

OOM后会在对应运行目录下生成dump快照文件,例如:java_pid33176.hprof

此时win+R ==> cmd ==> jvisualvm ==> 文件 ==> 装入该文件

一文搞定JVM调优

一文搞定JVM调优

可以看到此时main线程错误,String实例和占用内存最多,如果是对象实例过多,此处会显示对应的对象。

Arthas性能调优

官方地址:https://github.com/alibaba/arthas

官方教程:https://alibaba.github.io/arthas/arthas-tutorials

未完待续~~