JVM调优工具锦囊 (下)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: JVM调优工具锦囊

5. 老年代垃圾回收统计


命令:


jstat -gcold 进程ID

1187916-20211112180515374-586913543.png参数含义:


  • MC:方法区大小
  • MU:方法区已使用大小
  • CCSC:压缩指针类空间大小
  • CCSU:压缩类空间已使用大小
  • OC:老年代大小
  • OU:老年代已使用大小
  • YGC:年轻代垃圾回收次数
  • FGC:老年代垃圾回收次数
  • FGCT:老年代垃圾回收消耗时间
  • GCT:垃圾回收消耗总时间,新生代+老年代


6. 老年代内存统计


命令:


jstat -gcoldcapacity 进程ID


1187916-20211112180805153-654037985.png


参数含义:


  • OGCMN:老年代最小容量
  • OGCMX:老年代最大容量
  • OGC:当前老年代大小
  • OC:老年代大小
  • YGC:年轻代垃圾回收次数
  • FGC:老年代垃圾回收次数
  • FGCT:老年代垃圾回收消耗时间
  • GCT:垃圾回收消耗总时间


7. 元数据空间统计


命令

jstat -gcmetacapacity 进程ID

1187916-20211112180954514-1304020207.png


  • MCMN:最小元数据容量
  • MCMX:最大元数据容量
  • MC:当前元数据空间大小
  • CCSMN:最小指针压缩类空间大小
  • CCSMX:最大指针压缩类空间大小
  • CCSC:当前指针压缩类空间大小
  • YGC:年轻代垃圾回收次数
  • FGC:老年代垃圾回收次数
  • FGCT:老年代垃圾回收消耗时间
  • GCT:垃圾回收消耗总时间


8.整体运行情况


命令:


jstat -gcutil 进程ID

1187916-20211112181313350-215766105.png

  • S0:Survivor 1区当前使用比例
  • S1:Survivor 2区当前使用比例
  • E:Eden区使用比例
  • O:老年代使用比例
  • M:元数据区使用比例
  • CCS:指针压缩使用比例
  • YGC:年轻代垃圾回收次数
  • YGCT:年轻代垃圾回收消耗时间
  • FGC:老年代垃圾回收次数
  • FGCT:老年代垃圾回收消耗时间
  • GCT:垃圾回收消耗总时间


通过查询上面的参数来分析整个堆空间。


二、Arthas线上分析工具的使用



Arthas的功能非常强大,现附上官方文档:https://arthas.aliyun.com/doc/

其实想要了解Arthas,看官方文档就可以了,功能全而详细。那为什么还要整理一下呢?我们这里整理的是一些常用功能,以及在紧急情况下可以给我们帮大忙的功能。


Arthas分为几个部分来研究,先来看看我们的研究思路哈


1.安装及启动---这一块简单看,对于程序员来说,so easy

2.dashboard仪表盘功能---类似于JDK的jstat命令,

3.thread命令查询进行信息---类似于jmap命令

4.反编译线上代码----这个功能很牛,改完发版了,怎么没生效,反编译看看。

5.查询某一个函数的返回值

6.查询jvm信息,并修改----当发生内存溢出是,可以手动设置打印堆日志到文件

7.profiler火焰图


下面就来看看Arthas的常用功能的用法吧


1、Arthas的安装及启动


其实说到这快,不得不提的是,之前我一直因为arthas是一个软件,要启动,界面操作。当时我就想,要是这样,在线上安装一个单独的应用,公司肯定不同意啊~~~,研究完才发现,原来Arthas就是一个jar包。运行起来就是用java -jar 就可以。


1) 安装


可以直接在Linux上通过命令下载:


wget https://alibaba.github.io/arthas/arthas-boot.jar

也可以在浏览器直接访问https://alibaba.github.io/arthas/arthas-boot.jar,等待下载成功后,上传到Linux服务器上。


2) 启动


执行命令就可以启动了


java -jar arthas-boot.jar


启动成功可以看到如下界面:

1187916-20220107104821360-1633394556.png

然后找到你想监控的进程,输入前面对应的编号,就可以开启进行监控模式了。比如我要看4

1187916-20220107104943362-1751704249.png


看到这个就表示,进入应用监听成功


2、dashboard仪表盘--查询整体项目运行情况

执行命令

dashboard

1187916-20220107110157477-1104228179.png

这里面一共有三块


1)线程信息


我们可以看到当前进程下所有的线程信息。其中第13,14号线程当前处于BLOCKED阻塞状态,阻塞时间也可以看到。通过这个一目了然,当前有两个线程是有问题的,处于阻塞状态GC线程有6个。


2)内存信息


内存信息包含三个部分:堆空间信息、非堆空间信息和GC垃圾收集信息


堆空间信息


  • g1_eden_space: Eden区空间使用情况
  • g1_survivor_space: Survivor区空间使用情况
  • g1_old_gen: Old老年代空间使用情况


非堆空间信息


  • codeheap_'non-nmethods': 非方法代码堆大小
  • metaspace: 元数据空间使用情况
  • codeheap_'profiled_nmethods':
  • compressed_class_space: 压缩类空间使用情况


GC垃圾收集信息


  • gc.g1_young_generation.count:新生代gc的数量
  • gc.g1_young_generation.time(ms)新生代gc的耗时
  • gc.g1_old_generation.count: 老年代gc的数量
  • gc.g1_old_generation.time(ms):老年代gc的耗时


3) 运行时信息


  • os.name:当前使用的操作系统 Mac OS X
  • os.version :操作系统的版本号 10.16
  • java.version:java版本号 11.0.2
  • java.home:java根目录 /Library/Java/JavaVirtualMachines/jdk-11.0.2.jdk/Contents/Home
  • systemload.average:系统cpu负载平均值4.43

load average值的含义


> 单核处理器

假设我们的系统是单CPU单内核的,把它比喻成是一条单向马路,把CPU任务比作汽车。当车不多的时候,load <1;当车占满整个 马路的时候 load=1;当马路都站满了,而且马路外还堆满了汽车的时候,load>1

aHR0cDovL2RsLml0ZXllLmNvbS91cGxvYWQvYXR0YWNobWVudC8wMDYxLzk0ODcvMjE3M2EwNGQtZTQzZi0zNWJiLTg5MzMtMjI3Mjc5NjBkNTRmLnBuZw.png

aHR0cDovL2RsLml0ZXllLmNvbS91cGxvYWQvYXR0YWNobWVudC8wMDYxLzk0ODUvMTBhYjQwMjYtYjExMi0zOWRmLThhZDEtMjFiNWI1YjVkZTFhLnBuZw.png

aHR0cDovL2RsLml0ZXllLmNvbS91cGxvYWQvYXR0YWNobWVudC8wMDYxLzk0ODkvNTkxNjJmNTYtMThlMi0zM2QzLTgwYmMtYjhlYzcwNDViMzBkLnBuZw.png


> 多核处理器

我们经常会发现服务器Load > 1但是运行仍然不错,那是因为服务器是多核处理器(Multi-core)。

假设我们服务器CPU是2核,那么将意味我们拥有2条马路,我们的Load = 2时,所有马路都跑满车辆。

aHR0cDovL2RsLml0ZXllLmNvbS91cGxvYWQvYXR0YWNobWVudC8wMDYxLzk0OTUvMmRhMDQ2YjUtMGYyYS0zNTE5LWI0MWYtZjYxZmU1NmVlNDQzLnBuZw.pngLoad = 2时马路都跑满了

  • processors : 处理器个数 8
  • timestamp/uptime:采集的时间戳Fri Jan 07 11:36:12 CST 2022/2349s


通过仪表盘,我们能从整体了解当前线程的运行健康状况


3.thread命令查询CPU使用率最高的线程及问题原因


通过dashboard我们可以看到当前进程下运行的所有的线程。那么如果想要具体查看某一个线程的运行情况,可以使用thread命令


1. 统计cpu使用率最高的n个线程


先来看看常用的参数。


参数说明

参数名称 参数说明
id 线程id
[n:] 指定最忙的前N个线程并打印堆栈
[b] 找出当前阻塞其他线程的线程
[i <value>] 指定cpu使用率统计的采样间隔,单位为毫秒,默认值为200
[--all] 显示所有匹配的线程

我们的目标是想要找出CPU使用率最高的n个线程。那么需要先明确,如何计算出CPU使用率,然后才能找到最高的。计算规则如下:


首先、第一次采样,获取所有线程的CPU时间(调用的是java.lang.management.ThreadMXBean#getThreadCpuTime()及sun.management.HotspotThreadMBean.getInternalThreadCpuTimes()接口)
然后、睡眠等待一个间隔时间(默认为200ms,可以通过-i指定间隔时间)
再次、第二次采样,获取所有线程的CPU时间,对比两次采样数据,计算出每个线程的增量CPU时间
  线程CPU使用率 = 线程增量CPU时间 / 采样间隔时间 * 100%
注意: 这个统计也会产生一定的开销(JDK这个接口本身开销比较大),因此会看到as的线程占用一定的百分比,为了降低统计自身的开销带来的影响,可以把采样间隔拉长一些,比如5000毫秒。

统计1秒内cpu使用率最高的n个线程:


thread -n 3 -i 1000

1187916-20220107143447239-711062894.png

从线程的详情可以分析出,目前第一个线程的使用率是最高的,cpu占用了达到99.38%。第二行告诉我们,是Arthas.java这个类的第38行导致的。


由此,我们可以一眼看出问题,然后定位问题代码的位置,接下来就是人工排查问题了。

2、查询出当前被阻塞的线程


命令:

thread -b

1187916-20220107144310854-5320550.png


可以看到内容提示,线程Thread-1被线程Thread-0阻塞。对应的代码行数是DeadLockTest.java类的第31行。根据这个提示去查找代码问题。


3、指定采样的时间间隔


命令

thread -i 1000

这个的含义是个1s统计一次采样


4.反编译线上代码----这个功能很牛,改完发版了,怎么没生效,反编译看看。


说道Arthas,不得不提的一个功能就是线上反编译代码的功能。经常会发生的一种状况是,线上有问题,定位问题后立刻改代码,可是发版后发现没生效,不可能啊~~~刚刚提交成功了呀。于是重新发版,只能靠运气,不知道为啥没生效。


反编译线上代码可以让我们一目了然知道代码带动部分是否生效。反编译代码使用Arthas的jad命令


jad 命令将JVM中实际运行的class的byte code反编译成java代码
JAVA 复制 全屏

用法:


jad com.lxl.jvm.DeadLockTest

运行结果:

1187916-20220107150003941-1129592648.png


运行结果分析:这里包含3个部分


  • ClassLoader:类加载器就是加载当前类的是哪一个类加载器
  • Location: 类在本地保存的位置
  • 源码:类反编译字节码后的源码

如果不想想是类加载信息和本地位置,只想要查看类源码信息,可以增加--source-only参数


jad --source-only 类全名

6. ognl 动态执行线上的代码


能够调用线上的代码,是不是很神奇了。感觉哪段代码执行有问题,但是又没有日志,就可以使用这个方法动态调用目标方法了。


我们下面的案例都是基于这段代码执行,User类:

public class User {
    private int id;
    private String name;
    public User() {
    }
    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

DeadLockTest类:

public class DeadLockTest {
    private static Object lock1 = new Object();
    private static Object lock2 = new Object();
    private static List<String> names = new ArrayList<>();
    private List<String> citys = new ArrayList<>();
    public static String add() {
        names.add("zhangsan");
        names.add("lisi");
        names.add("wangwu");
        names.add("zhaoliu");
        return "123456";
    }
    public List<String> getCitys() {
        DeadLockTest deadLockTest = new DeadLockTest();
        deadLockTest.citys.add("北京");
        return deadLockTest.citys;
    }
    public static List<User> addUsers(Integer id, String name) {
        List<User> users = new ArrayList<>();
        User user = new User(id, name);
        users.add(user);
        return users;
    }
    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (lock1) {
                try {
                    System.out.println("thread1 begin");
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                }
                synchronized (lock2) {
                    System.out.println("thread1 end");
                }
            }
        }).start();
        new Thread(() -> {
            synchronized (lock2) {
                try {
                    System.out.println("thread2 begin");
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                }
                synchronized (lock1) {
                    System.out.println("thread2 end");
                }
            }
        }).start();
    }
}

1)获取静态函数


> 返回值是字符串


ognl '@全路径类名@静态方法名("参数")'

示例1:在DeadLockTest类中有一个add静态方法,我们来看看通过ognl怎么执行这个静态方法。执行命令


ognl '@com.lxl.jvm.DeadLockTest@add()'
其中,第一个@后面跟的是类的全名称;第二个@跟的是属性或者方法名,如果属性是一个对象,想要获取属性里面的属性或者方法,直接打.属性名/方法名 即可。

运行效果:

1187916-20220107170628633-1698818060.png

我们看到了这个对象的返回值是123456


> 返回值是对象


ognl '@全路径类名@静态方法名("参数")' -x 2

这里我们可以尝试一下替换-x 2 为 -x 1 ;-x 3;


* 案例1:返回对象的地址。不加 -x 或者是-x 1


ognl '@com.lxl.jvm.DeadLockTest@addUsers(1,"zhangsan")'
ognl '@com.lxl.jvm.DeadLockTest@addUsers(1,"zhangsan")' -x 1


返回值1187916-20220110104653738-1356439116.png

* 案例2:返回对象中具体参数的值。加 -x 2


ognl '@com.lxl.jvm.DeadLockTest@addUsers(1,"zhangsan")' -x 2


返回值

1187916-20220110104913446-1073466270.png

* 案例3:返回对象中有其他对象


  • 命令:
ognl '@com.lxl.jvm.DeadLockTest@addUsers(1,"zhangsan")' -x 2

执行结果:

1187916-20220110112815542-15845671.png


-x 2 获取的是对象的值,List返回的是数组信息,数组长度。


  • 命令:


ognl '@com.lxl.jvm.DeadLockTest@addUsers(1,"zhangsan")' -x 3

执行结果:

1187916-20220110113107309-833028981.png

-x 3 打印出对象的值,对象中List列表中的值。


* 案例4:方法A的返回值当做方法B的入参


ognl '#value1=@com.lxl.jvm.DeadLockTest@getCitys(), #value2=@com.lxl.jvm.DeadLockTest@generatorUser(1,"lisi",#value1), {#value1,#value2}' -x 2

1187916-20220110115606917-1602312378.png

> 方法入参是简单类型的列表

ognl '@com.lxl.jvm.DeadLockTest@returnCitys({"beijing","shanghai","guangdong"})'

1187916-20220110120214304-1869604068.png


> 方法入参是一个map对象

ognl '#value1=new com.lxl.jvm.User(1,"zhangsan"), #value1.setCitys({"bj", "sh"}), #value2=#{"mum":"zhangnvshi","dad":"wangxiansheng"}, #value1.setFamily(#value2), #value1' -x 2

1187916-20220110143502104-1220353225.png


2)获取静态字段


ognl '@全路径类名@静态属性名'

示例:在DeadLockTest类中有一个names静态属性,下面来看看如何获取这个静态属性。执行命令:


ognl '@com.lxl.jvm.DeadLockTest@names'
其中,第一个@后面跟的是类的全名称;第二个@跟的是属性或者方法名,如果属性是一个对象,想要获取属性里面的属性或者方法,直接打.属性名/方法名 即可。

运行效果:

1187916-20220107171613413-114494838.png

第一次执行获取属性命令,返回的属性是一个空集合;然后执行add方法,往names集合中添加了属性;再次请求names集合,发现有4个属性返回。


3) 获取实例对象


ognl '#value1=new com.lxl.jvm.User(1,"zhangsan"),#value1.setName("aaa"), #value1.setCitys({"bj", "sh"}), {#value1}' -x 2

获取实例对象,使用new关键字,执行结果:


1187916-20220110142724069-1338859433.png

7. 线上代码修改


生产环境有时会遇到非常紧急的问题,或突然发现一个bug,这时候不方便重新发版,或者发版未生效,可以使用Arthas临时修改线上代码。通过Arthas修改的步骤如下:


1. 从读取.class文件
2. 编译成.java文件
3. 修改.java文件
4. 将修改后的.java文件编译成新的.class文件
5. 将新的.class文件通过classloader加载进JVM内

第一步:读取.class文件


sc -d *DeadLockTest*

使用sc命令查看JVM已加载的类信息。关于sc命令,查看官方文档:

https://arthas.aliyun.com/doc/sc.html


  • -d : 表示打印类的详细信息

1187916-20220110152237592-2103219740.png

最后一个参数classLoaderHash,表示在jvm中类加载的hash值,我们要获得的就是这个值。


第二步:使用jad命令将.class文件反编译为.java文件才行

jad -c 7c53a9eb --source-only com.lxl.jvm.DeadLockTest > /Users/lxl/Downloads/DeadLockTest.java
  • jad命令是反编译指定已加载类的源码
  • -c : 类所属 ClassLoader 的 hashcode
  • --source-only:默认情况下,反编译结果里会带有ClassLoader信息,通过--source-only选项,可以只打印源代码。
  • com.lxl.jvm.DeadLockTest:目标类的全路径
  • /Users/lxl/Downloads/DeadLockTest.java:反编译文件的保存路径
/*
        * Decompiled with CFR.
        * 
        * Could not load the following classes:
        *  com.lxl.jvm.User
        */
       package com.lxl.jvm;
       import com.lxl.jvm.User;
       import java.util.ArrayList;
       import java.util.List;
       public class DeadLockTest {
           private static Object lock1 = new Object();
           private static Object lock2 = new Object();
           private static List<String> names = new ArrayList<String>();
           private List<String> citys = new ArrayList<String>();
           public static List<String> getCitys() {
               DeadLockTest deadLockTest = new DeadLockTest();
/*25*/         deadLockTest.citys.add("北京");
/*27*/         return deadLockTest.citys;
           }
          ......
           public static void main(String[] args) {
              ......
           }
       }

这里截取了部分代码。


第三步:修改java文件


public static List<String> getCitys() {
               System.out.println("-----这里增加了一句日志打印-----");
               DeadLockTest deadLockTest = new DeadLockTest();
/*25*/         deadLockTest.citys.add("北京");
/*27*/         return deadLockTest.citys;
           }

第四步:使用mc命令将.java文件编译成.class文件


mc -c 512ddf17 -d /Users/luoxiaoli/Downloads /Users/luoxiaoli/Downloads/DeadLockTest.java
  • mc: 编译.java文件生.class文件, 详细使用方法参考官方文档

https://arthas.aliyun.com/doc/mc.html


  • -c:指定classloader的hash值
  • -d:指定输出目录
  • 最后一个参数是java文件路径


这是反编译后的class字节码文件

1187916-20220110155806837-1061384912.png

第五步:使用redefine命令,将.class文件重新加载进JVM


redefine -c /Users/***/Downloads/com/lxl/jvm/DeadLockTest.class

1187916-20220110160640937-1900626451.png

最后看到redefine success,表示重新加载.class文件进JVM成功了。


注意事项

redefine命令使用之后,再使用jad命令会使字节码重置,恢复为未修改之前的样子。官方关于redefine命令的说明

第六步:检验效果


这里检测效果,调用接口,执行日志即可。


8、实时修改生产环境的日志级别


这个功能也很好用,通常,我们在日志中打印的日志级别一般是infor、warn、error级别的,debug日志一般看不到。那么出问题的时候,一些日志,在写代码的时候会被记录在debug日志中,而此时日志级别又很高。那么迫切需要调整日志级别。

这个功能很好用啊,我们可以将平时不经常打印出来的日志设置为debug级别。设置线上日志打印级别为info。当线上有问题的时候,可以将日志级别动态调整为debug。异常排查完,在修改回info。这对访问量特别大日志内容很多的项目比较有效,可以有效节省日志输出带来的开销。


第一步:使用logger命令查看日志级别

1187916-20220110171348231-1407393276.png

  • 当前应用的日志级别是info
  • 类加载的hash值是18b4aac2


我们定义一个接口,其源代码内容如下:


@PostMapping(value = "test")
    public String test() {
        log.debug("这是一条 debug 级别的日志");
        log.info("这是一条 info 级别的日志");
        log.error("这是一条 error 级别的日志");
        log.warn("这是一条 warn 级别的日志");
        return "完成";
    }


可以调用接口,查看日志输出代码。

1187916-20220110171803519-1888991937.png


我们看到,日志输出的是info及以下的级别。


第二步:修改logger日志的级别


logger -c 18b4aac2 --name ROOT --level debug

修改完日志级别以后,输出日志为debug级别。

1187916-20220110171558258-440670662.png

8. 查询jvm信息,并修改----当发生内存溢出时,可以手动设置打印堆日志到文件


通常查询jvm参数,使用的是Java自带的工具[jinfo 进程号]。arthas中通过vmoption获取jvm参数:

1187916-20220110172946528-1674039717.png


假设,我们要设置JVM出现OutOfMemoryError的时候,自动dump堆快照

vmoption  HeapDumpOnOutOfMemoryError true

1187916-20220110173112269-1641458826.png

这时,如果发生堆内存溢出,会打印日志到文件


9. 监控函数耗时

trace 待监控方法的全类名  待监控的方法名
trace com.lxl.jvm.DeadLockTest generatorUser


660539123fe94ea3b8e775296a0c2701.jpg


  • 通过圈起来的部分可以看到,接口的入口函数time总耗时371ms
  • 其中getDataFromDb函数耗时200ms
  • getDataFromRedis函数耗时100ms
  • getDataFromOuter函数耗时50ms
  • process函数耗时20ms


很明显,最慢的函数已经找到了,接下里就要去对代码进行进一步分析,然后再进行优化


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
8天前
|
Arthas 监控 Java
JVM进阶调优系列(9)大厂面试官:内存溢出几种?能否现场演示一下?| 面试就那点事
本文介绍了JVM内存溢出(OOM)的四种类型:堆内存、栈内存、元数据区和直接内存溢出。每种类型通过示例代码演示了如何触发OOM,并分析了其原因。文章还提供了如何使用JVM命令工具(如jmap、jhat、GCeasy、Arthas等)分析和定位内存溢出问题的方法。最后,强调了合理设置JVM参数和及时回收内存的重要性。
|
6天前
|
监控 Java 编译器
Java虚拟机调优实战指南####
本文深入探讨了Java虚拟机(JVM)的调优策略,旨在帮助开发者和系统管理员通过具体、实用的技巧提升Java应用的性能与稳定性。不同于传统摘要的概括性描述,本文摘要将直接列出五大核心调优要点,为读者提供快速预览: 1. **初始堆内存设置**:合理配置-Xms和-Xmx参数,避免频繁的内存分配与回收。 2. **垃圾收集器选择**:根据应用特性选择合适的GC策略,如G1 GC、ZGC等。 3. **线程优化**:调整线程栈大小及并发线程数,平衡资源利用率与响应速度。 4. **JIT编译器优化**:利用-XX:CompileThreshold等参数优化即时编译性能。 5. **监控与诊断工
|
17天前
|
存储 监控 Java
JVM进阶调优系列(8)如何手把手,逐行教她看懂GC日志?| IT男的专属浪漫
本文介绍了如何通过JVM参数打印GC日志,并通过示例代码展示了频繁YGC和FGC的场景。文章首先讲解了常见的GC日志参数,如`-XX:+PrintGCDetails`、`-XX:+PrintGCDateStamps`等,然后通过具体的JVM参数和代码示例,模拟了不同内存分配情况下的GC行为。最后,详细解析了GC日志的内容,帮助读者理解GC的执行过程和GC处理机制。
|
25天前
|
Arthas 监控 数据可视化
JVM进阶调优系列(7)JVM调优监控必备命令、工具集合|实用干货
本文介绍了JVM调优监控命令及其应用,包括JDK自带工具如jps、jinfo、jstat、jstack、jmap、jhat等,以及第三方工具如Arthas、GCeasy、MAT、GCViewer等。通过这些工具,可以有效监控和优化JVM性能,解决内存泄漏、线程死锁等问题,提高系统稳定性。文章还提供了详细的命令示例和应用场景,帮助读者更好地理解和使用这些工具。
|
30天前
|
监控 架构师 Java
JVM进阶调优系列(6)一文详解JVM参数与大厂实战调优模板推荐
本文详述了JVM参数的分类及使用方法,包括标准参数、非标准参数和不稳定参数的定义及其应用场景。特别介绍了JVM调优中的关键参数,如堆内存、垃圾回收器和GC日志等配置,并提供了大厂生产环境中常用的调优模板,帮助开发者优化Java应用程序的性能。
|
1月前
|
存储 监控 算法
JVM调优深度剖析:内存模型、垃圾收集、工具与实战
【10月更文挑战第9天】在Java开发领域,Java虚拟机(JVM)的性能调优是构建高性能、高并发系统不可或缺的一部分。作为一名资深架构师,深入理解JVM的内存模型、垃圾收集机制、调优工具及其实现原理,对于提升系统的整体性能和稳定性至关重要。本文将深入探讨这些内容,并提供针对单机几十万并发系统的JVM调优策略和Java代码示例。
49 2
|
1月前
|
Java
JVM进阶调优系列(5)CMS回收器通俗演义一文讲透FullGC
本文介绍了JVM中CMS垃圾回收器对Full GC的优化,包括Stop the world的影响、Full GC触发条件、GC过程的四个阶段(初始标记、并发标记、重新标记、并发清理)及并发清理期间的Concurrent mode failure处理,并简述了GC roots的概念及其在GC中的作用。
|
1月前
|
存储 安全 Java
jvm 锁的 膨胀过程?锁内存怎么变化的
【10月更文挑战第3天】在Java虚拟机(JVM)中,`synchronized`关键字用于实现同步,确保多个线程在访问共享资源时的一致性和线程安全。JVM对`synchronized`进行了优化,以适应不同的竞争场景,这种优化主要体现在锁的膨胀过程,即从偏向锁到轻量级锁,再到重量级锁的转变。下面我们将详细介绍这一过程以及锁在内存中的变化。
37 4
|
5天前
|
Java Linux Windows
JVM内存
首先JVM内存限制于实际的最大物理内存,假设物理内存无限大的话,JVM内存的最大值跟操作系统有很大的关系。简单的说就32位处理器虽然可控内存空间有4GB,但是具体的操作系统会给一个限制,这个限制一般是2GB-3GB(一般来说Windows系统下为1.5G-2G,Linux系统下为2G-3G),而64bit以上的处理器就不会有限制。
8 1
|
1月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
63 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS