Java学习-JVM调优

news2024/10/10 21:13:13

目录

1. JDK工具包

1.1 jps

1.2 jstat

1.3 jinfo

1.4 jmap

1.5 jhat

1.6 jstack

1.7 VisualVM

2. 第三方工具

2.1 GCEasy

2.2 MAT

2.3 Arthas

3. JVM参数

3.1 标准参数

3.2 非标准参数

3.3 不稳定参数

4. 调优

4.1 什么时候调优

4.2 调优调什么

4.3 调优原则

4.4 调优步骤

4.5 调优参数设置

4.6 Young GC 日志含义

4.7 FullGC 日志含义

4.8 堆内存跟元空间优化

4.9 线程堆栈优化

4.10 垃圾回收器优化

4.11 案例


1. JDK工具包

jvm

1.1 jps

查看Java进程 ,相当于Linux下的ps命令,只不过它只列出Java进程。

jps :列出Java程序进程ID和Main函数名称
jps -q :只输出进程ID
jps -m :输出传递给Java进程(主函数)的参数
jps -l :输出主函数的完整路径
jps -v :显示传递给Java虚拟的参数

 

1.2 jstat

jstat可以查看Java程序运行时相关信息,可以通过它查看运行时堆信息的相关情况。

jstat -<options> [-t] [-h<lines>] <vmid> [<interval> [<count>]]

options:由以下值构成
-class:显示ClassLoader的相关信息
-compiler:显示JIT编译的相关信息
-gc:显示与GC相关信息
-gccapacity:显示各个代的容量和使用情况
-gccause:显示垃圾收集相关信息(同-gcutil),同时显示最后一次或当前正在发生的垃圾收集的诱发原因
-gcnew:显示新生代信息
-gcnewcapacity:显示新生代大小和使用情况
-gcold:显示老年代信息
-gcoldcapacity:显示老年代大小
-gcpermcapacity:显示永久代大小
-gcutil:显示垃圾收集信息

jstat -gc 18551 250 4

# 进程ID 34784 ,采样间隔250ms,采样数4

S0C:年轻代中第一个survivor(幸存区)的容量 (单位kb)  
S1C:年轻代中第二个survivor(幸存区)的容量 (单位kb)  
S0U :年轻代中第一个survivor(幸存区)目前已使用空间 (单位kb)  
S1U :年轻代中第二个survivor(幸存区)目前已使用空间 (单位kb)  
EC :年轻代中Eden的容量 (单位kb)  

EU :年轻代中Eden目前已使用空间 (单位kb)  
OC :Old代的容量 (单位kb)  
OU :Old代目前已使用空间 (单位kb)  
MC:metaspace的容量 (单位kb)  
MU:metaspace目前已使用空间 (单位kb)  
CCSC:压缩类空间大小  
CCSU:压缩类空间使用大小  
YGC :从应用程序启动到采样时年轻代中gc次数  
YGCT :从应用程序启动到采样时年轻代中gc所用时间(s)  
FGC :从应用程序启动到采样时old代(全gc)gc次数  
FGCT :从应用程序启动到采样时old代(全gc)gc所用时间(s)  
GCT:从应用程序启动到采样时gc用的总时间(s)  

jvm

jstat -gcutil

下面输出的是进程内存区域百分百 及 GC详细信息

jstat -gcutil 30108 1s 5
# 进程ID 30108,采样间隔1s,采样数5

S0 年轻代中第一个survivor(幸存区)已使用的占当前容量百分比
S1 年轻代中第二个survivor(幸存区)已使用的占当前容量百分比
E 年轻代中Eden(伊甸园)已使用的占当前容量百分比
O old代已使用的占当前容量百分比
M metaspace已使用的占当前容量百分比
CCS 压缩使用比例
YGC 从应用程序启动到采样时年轻代中gc次数
YGCT 从应用程序启动到采样时年轻代中gc所用时间(s)
FGC 从应用程序启动到采样时old代(全gc)gc次数
FGCT 从应用程序启动到采样时old代(全gc)gc所用时间(s)
GCT 从应用程序启动到采样时gc用的总时间(s)

jvm

 

1.3 jinfo

jinfo可以用来查看正在运行的Java程序的扩展参数,甚至支持修改运行过程中的部分参数

jinfo [option] <pid>

-flags 打印虚拟机 VM 参数
-flag <name> 打印指定虚拟机 VM 参数
-flag [+|-]<name> 打开或关闭虚拟机参数
-flag <name>=<value> 设置指定虚拟机参数的值


jinfo -flags 18551

jvm

 

1.4 jmap

jmap用来查看堆内存使用状况,一般结合jhat使用。

jmap pid

描述:查看进程的内存映像信息。使用不带选项参数的jmap打印共享对象映射,将会打印目标虚拟机中加载的每个共享对象的 起始地址、映射大小以及共享对象文件的路径全称。

jmap -heap pid

显示Java堆详细信息:打印堆的摘要信息,包括使用的GC算法、堆配置信息和各内存区域内存使用信息

jmap -histo:live pid

描述:显示堆中对象的统计信息:其中包括每个Java类、对象数量、内存大小(单位:字节)、完全限定的类名。打印的虚拟机内 部的类名称将会带有一个’*’前缀。如果ja指定了live子选项,则只计算活动的对象

jmap -clstats pid

描述:打印类加载器信息:打印Java堆内存的方法区的类加载器的智能统计信息。对于每个类加载器而言,它的名称、活跃度、 地址、父类加载器、它所加载的类的数量和大小都会被打印。此外,包含的字符串数量和大小也会被打印。

jmap -finalizerinfo pid

描述:打印等待终结的对象信息

jmap -dump:format=b,file=heapdump.hprof pid

描述:生成堆转储快照dump文件:以二进制格式转储Java堆到指定文件中。如果指定了live子选项,堆中只有活动的对象会被转 储。浏览heap dump 可以使用jhat 读取生成的文件,也可以使用MAT等堆内存分析工具。

1.5 jhat

jhat 命令会解析Java堆转储文件,并启动一个 web server。然后用浏览器来查看/浏览 dump 出来的 heap二进制文件。
jhat 命令支持预先设计的查询,比如:显示某个类的所有实例。还支持 对象查询语言(OQL)。 OQL有点类似SQL,专门用来 查询堆转储。

Java生成堆转储的方式有多种:

  1. 使用 jmap -dump 选项可以在JVM运行时获取 dump.
  2. 使用 jconsole 选项通过 HotSpotDiagnosticMXBean 从运行时获得堆转储。
  3. 在虚拟机启动时如果指定了 -XX:+HeapDumpOnOutOfMemoryError 选项,则抛出 OutOfMemoryError 时,会自动执行堆转 储

jhat [ options ] heap-dump-file

1.6 jstack

stack是Java虚拟机自带的一种堆栈跟踪工具,用于生成java虚拟机当前时刻的线程快照。

线程快照是当前Java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因, 如线程间死锁、死循环、请求外部资源导致的长时间等待、等等。

线程快照里留意下面几种状态

死锁,Deadlock(重点关注)
等待资源,Waiting on condition(重点关注)
等待获取管程,Waiting on monitor entry(重点关注)
阻塞,Blocked(重点关注)
执行中,Runnable
暂停,Suspended
对象等待中,Object.wait() 或 TIMED_WAITING
停止,Parked

jstack [ option ] pid 查看当前时间点,指定进程的dump堆栈信息。
jstack [ option ] pid > 文件 将当前时间点的指定进程的dump堆栈信息,写入到指定文件中。
# 注:若该文件不存在,则会自动生成; 若该文件存在,则会覆盖源文件。
jstack [ option ] executable core 查看当前时间点,core文件的dump堆栈信息。
jstack [ option ] [server_id@]<remote server IP or hostname> 查看当前时间点,远程机器的dump堆栈信息。


-F # 当进程挂起了,此时'jstack [-l] pid'是没有相应的,这时候可使用此参数来强制打印堆栈信息,强制jstack),一般情况不
需要使用。
-m # 打印java和native c/c++框架的所有栈信息。可以打印JVM的堆栈,以及Native的栈帧,一般应用排查不需要使用。
-l # 长列表. 打印关于锁的附加信息。例如属于java.util.concurrent的ownable synchronizers列表,会使得JVM停顿得长久
得多(可能会差很多倍,比如普通的jstack可能几毫秒和一次GC没区别,加了-l 就是近一秒的时间),-l 建议不要用。一般情况不需
要使用。
-h or -hel # 打印帮助信息

jstack pid 打印堆栈

jstack -l 30108 | grep 'java.lang.Thread.State' | wc -l

 

1.7 VisualVM

VisualVM 是一款免费的性能分析工具。它通过 jvmstat、JMX、SA(Serviceability Agent)以及 Attach API 等多种方式从程序运行时 获得实时数据,从而进行动态的性能分析。同时,它能自动选择更快更轻量级的技术尽量减少性能分析对应用程序造成的影响,提高 性能分析的精度。

 

2. 第三方工具

2.1 GCEasy

业界首先采用机器学习算法解决GC日志分析问题,GCeasy内置机器智能可以自动检测JVM和Android GC日志中的问题,并推荐解决 方案。

https://gceasy.io/

 

2.2 MAT

MAT是一个强大的可视化内存分析工具,可以快捷、有效地帮助我们找到内存泄露,减少内存消耗分析工具。MAT是Memory Analyzer tool的缩写,是一种快速,功能丰富的Java堆分析工具,能帮助你查找内存泄漏和减少内存消耗。

功能:
找到最大的对象,因为MAT提供显示合理的累积大小(retained size)
探索对象图,包括inbound和outbound引用,即引用此对象的和此对象引出的
查找无法回收的对象,可以计算从垃圾收集器根到相关对象的路径
找到内存浪费,比如冗余的String对象,空集合对象。

MAT安装有两种方式,一种是以eclipse插件方式安装,一种是独立安装。
在MAT的官方文档中有相应的安装文件下载,下载地址为:https://www.eclipse.org/mat/downloads.php
eclipse插件安装,help -> install new soft点击ADD,在弹出框中添加插件地址:http://download.eclipse.org/mat/1.9.0/upda te-site/,也可以直接在下载页面下载离线插件包,以离线方式安装。
独立安装:下载解压包,解压即安装

 

2.3 Arthas

Arthas 是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下, 对业务问题进行诊断,包括查看方法调用的出入参、异常,监测方法执行耗时,类加载信息等,大大提升线上问题排查效率。

# 下载arthas-boot.jar
curl -O https://alibaba.github.io/arthas/arthas-boot.jar
# 启动
java -jar arthas-boot.jar
# 选择应用java进程:

[INFO] arthas-boot version: 3.5.3
[INFO] Found existing java process, please choose one and input the serial number of the
process, eg : 1. Then hit ENTER.
* [1]: 31480 org.apache.catalina.startup.Bootstrap
[2]: 30108 /root/hero_web-1.0-SNAPSHOT-default.jar
# Demo进程是第2个,则输入2,再输入回车/enter。Arthas会attach到目标进程上,并输出日志:
[INFO] Start download arthas from remote server: https://arthas.aliyun.com/download/3.5.4?
mirror=aliyun
[INFO] Download arthas success.
[INFO] arthas home: /root/.arthas/lib/3.5.4/arthas
[INFO] Try to attach process 30108
[INFO] Attach process 30108 success.
[INFO] arthas-client connect 127.0.0.1 3658
,---. ,------. ,--------.,--. ,--. ,---. ,---.
/ O \ | .--. ''--. .--'| '--' | / O \ ' .-'
| .-. || '--'.' | | | .--. || .-. |`. `-.
| | | || |\ \ | | | | | || | | |.-' |
`--' `--'`--' '--' `--' `--' `--'`--' `--'`-----'
wiki https://arthas.aliyun.com/doc
tutorials https://arthas.aliyun.com/doc/arthas-tutorials.html
version 3.5.4
main_class
pid 30108
time 2021-10-12 11:24:34
[arthas@30108]$

  • Arthas 常见命令
    jvm:查看当前 JVM 的信息
    thread:查看当前 JVM 的线程堆栈信息,
    -b 选项可以一键检测死锁
    -n 指定最忙的前N个线程并打印堆栈
    trace:方法内部调用路径,并输出方法路径上的每个节点上耗时,服务间调用时间过长时使用
    stack:输出当前方法被调用的调用路径
    Jad:反编译指定已加载类的源码,反编译便于理解业务
    logger:查看和修改 logger,可以动态更新日志级别

  • 查看dashboard

输入dashboard,按 回车/enter ,会展示当前进程的信息,按 ctrl+c 可以中断执行

jvm

  • 查看线程thread

通过thread命令来获取到应用进程的线程信息

thread -1 会打印线程统计信息。

jvm

  • 反编译已加载类源码

运行期通过jad来反编译项目代码

  • 监听运行时方法的返回值watch
    通过watch命令来查看 com.hero.web.user.controller#UserController 函数的返回值:
watch com.hero.web.user.controller.UserController findByUsername returnObj 
  • 退出
    如果只是退出当前的连接,可以用 quit 或者 exit 命令。Attach到目标进程上的arthas还会继续运行,端口会保持开放,下次连接时 可以直接连接上。如果想完全退出arthas,可以执行 stop 命令。

 

3. JVM参数

标准参数、非标准参数、不稳定参数

3.1 标准参数

顾名思义,标准参数中包括功能以及输出的结果都是很稳定的,基本上不会随着JVM版本的变化而变化。标准参数以-开 头,如:java -version、java -jar等,通过java -help可以查询所有的标准参数,我们可以通过 -help 命令来检索出所有标准参数

3.2 非标准参数

非标准参数:以-X开头,是标准参数的扩展。表示在将来的JVM版本中可能会发生改变,但是这类以-X开始的参数变化比较小。 可以通过 Java -X 命令来检索所有-X 参数。

比较常见的非标准参数:

  1. -Xms堆最小值:默认值是总内存/64(且小于1G),默认情况下,当堆中可用内存小于40%(-XX: MinHeapFreeRatio调整) 时,堆内存会开始增加,一直增加到-Xmx大小。
  2. -Xmx堆最大值:默认值是总内存/64(且小于1G),如果Xms和Xmx都不设置,则两者大小会相同,默认情况下,当堆中可用 内存大于70%时,堆内存会开始减少,一直减小到-Xms的大小;
  3. -Xmn新生代内存:默认是整堆的1/3,包括Eden区和两个Survivor区的总和,写法如: -Xmn1024,-Xmn1024k,-Xmn1024m,- Xmn1g 。新生代按整堆的比例分配,所以,此值不建议设置!
  4. -Xss线程栈内存:默认1M,一般来说是不需要改的。
  5. 打印GC日志:-Xloggc:file与-verbose:gc功能类似,只是将每次GC事件的相关情况记录到一个文件中。

3.3 不稳定参数

不稳定参数:这也是非标准化参数,相对来说不稳定,随着JVM版本的变化可能会发生变化,主要用于JVM调优和debug。 不稳定参数以-XX 开头,此类参数的设置很容易引起JVM 性能上的差异,使JVM存在极大的不稳定性。如果此类参数设置合理将大大提 高JVM的性能及稳定性。

不稳定参数分为三类:
性能参数:用于JVM的性能调优和内存分配控制,如内存大小的设置;
行为参数:用于改变JVM的基础行为,如GC的方式和算法的选择;
调试参数:用于监控、打印、输出jvm的信息;

不稳定参数语法规则:

  1. 布尔类型参数值:
    -XX:+
    '+'表示启用该选项
    -XX:-
    '-'表示关闭该选项
    示例:-XX:+UseG1GC:表示启用G1垃圾收集器
  2. 数字类型参数值:
    -XX:
    =给选项设置一个数字类型值,可跟随单位,例如:'m'或'M'表示兆字节;'k'或'K'千字节;'g'或'G'千兆字节。32K与32768是相同大小的。
    示例:-XX:MaxGCPauseMillis=500 :表示设置GC的最大停顿时间是500ms
  3. 字符串类型参数值:
    -XX:
    =给选项设置一个字符串类型值,通常用于指定一个文件、路径或一系列命令列表。
    示例:-XX:HeapDumpPath=./dump.core
    常用的不稳定参数:
    -XX:+UseSerialGC 配置串行收集器
    -XX:+UseParallelGC 配置PS并行收集器
    -XX:+UseParallelOldGC 配置PO并行收集器
    -XX:+UseParNewGC 配置ParNew并行收集器
    -XX:+UseConcMarkSweepGC 配置CMS并行收集器
    -XX:+UseG1GC 配置G1并行收集器
    -XX:+PrintGCDetails 配置开启GC日志打印
    -XX:+PrintGCTimeStamps 配置开启打印GC时间戳
    -XX:+PrintGCDateStamps 配置开启打印GC日期
    -XX:+PrintHeapAtGC 配置开启在GC时,打印堆内存信息

 

4. 调优

调优的最终目的都是为了应用程序使用最小的硬件消耗来承载更大的吞吐量。JVM调优主要是针对垃圾收集器的收集性能进行优化令 运行在虚拟机上的应用,能够使用更少的内存(Footprint),及更低的延迟(Latency),获取更大的吞吐量(Throughput)。

调优最终目标:

  • 堆内存使用率 <= 70%;
  • 老年代内存使用率<= 70%;
  • avg pause <= 1秒;
  • Full GC 次数0 或 avg pause interval >= 24小时 ;

 

4.1 什么时候调优

遇到以下情况,就需要考虑进行JVM调优:

  1. 系统吞吐量下降与响应延迟(P99);
  2. Heap内存(老年代)持续上涨至出现OOM;
  3. Full GC 次数频繁;
  4. GC 停顿过长(超过1秒);
  5. 应用出现OutOfMemory 等内存异常;
  6. 应用中有使用本地缓存且占用大量内存空间;

 

4.2 调优调什么

内存分配 + 垃圾回收!

  1. 合理使用堆内存
  2. GC高效回收占用的内存的垃圾对象
  3. GC高效释放掉内存空间

 

4.3 调优原则

优先原则:优先架构调优和代码调优,JVM优化是不得已的手段
大多数的Java应用不需要进行JVM优化
观测性原则:发现问题解决问题,没有问题不找问题

 

4.4 调优步骤

第一步:监控分析GC日志
第二步:判断JVM问题:
如果各项参数设置合理,系统没有超时日志出现,GC频率不高,GC耗时不高,那么没有必要进行GC优化 如果GC时间超过1秒,或者频繁GC,则必须优化。
第三步:确定调优目标
第四步:调整参数
调优一般是从满足程序的内存使用需求开始,之后是时间延迟需求,最后才是吞吐量要求,要基于这个步骤来不断优化,每 一个步骤都是进行下一步的基础,不可逆行之。
第五步:对比调优前后差距
第六步:重复:1、2、3、4、5步骤
找到最佳JVM参数设置
第七步:应用JVM参数到应用服务器:
找到最合适的参数,将这些参数灰度发布一部分机器,观察一段时间。
如果,观察结果可以验证方案的价值,则进行全量发布!

 

4.5 调优参数设置

JVM调优典型参数设置:
-Xms堆内存最小值
-Xmx堆内存最大值
-Xmn新生代内存的最大值
-Xss每个线程的栈内存

# 计算最大线程数的公式:理论上限
Number of threads = (MaxProcess内存 - JVM内存 - ReservedOsMemory) / (ThreadStackSize)
系统最大可创建的线程数量=(机器本身可用内存 - (JVM分配的堆内存+JVM元数据区)) / Xss的值

如果想要确定JVM性能问题瓶颈,需要分析GC日志

  1. -XX:+PrintGCDetails 开启GC日志创建更详细的GC日志,默认关闭
  2. -XX:+PrintGCTimeStamps,-XX:+PrintGCDateStamps 开启GC时间提示, 开启时间便于我们更精确地判断几次GC操作之间的时两个参数的区别
    时间戳是相对于0(依据JVM启动的时间)的值,而日期戳(date stamp)是实际的日期字符串 由于日期戳需要进行格式化,所以它的效率可能会受轻微的影响,不过这种操作并不频繁,它造成的影响也很难被我们感 知。
  3. -XX:+PrintHeapAtGC 打印堆的GC日志
  4. -Xloggc:./logs/gc.log 指定GC日志路径
# 配置GC日志输出
JAVA_OPT="${JAVA_OPT} -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -
XX:+PrintHeapAtGC -Xloggc:${BASE_DIR}/logs/gc-default.log "

 

4.6 Young GC 日志含义

2021-05-18T14:31:11.340+0800: 2.340: [GC (Allocation Failure) [PSYoungGen: 896512K->41519K(1045504K)]
896512K-41543K(3435008K), 0.0931965 secs] [Times: user=0.14 sys=0.02, real=0.10 secs]


# 第一部分
2021-05-18T14:31:11.340+0800: 2.340: [GC (Allocation Failure)
GC事件开始时间,+0800代表中国所在的东区:2021-05-18T14:31:11.340+0800
GC事件开始时间相对于JVM开始启动的间隔秒数:2.340
区分YoungGC还是FullGC的标志,GC代表YoungGC
触发GC的原因: (Allocation Failure)
# 第二部分
[PSYoungGen: 896512K->41519K(1045504K)] 896512K-41543K(3435008K), 0.0931965 secs]
垃圾收集器的名称:PSYoungGen
在垃圾收集之前 和 之后新时代的使用量:896512K->41519K
新生代总空间大小:(1045504K)
在垃圾收集之前和之后整个堆内存使用量:896512K-41543K
堆总空间大小:(3435008K)
GC持续时间:0.0931965 secs

# 第三部分
[Times: user=0.14 sys=0.02, real=0.10 secs]
GC线程消耗的CPU时间:user=0.14
GC过程中操作系统调用和系统等待事件所消耗的时间:sys=0.02
应用程序暂停的时间:0.10 secs

 

4.7 FullGC 日志含义

2021-05-19T14:46:07.367+0800: 1.562: [Full GC (Metadata GC Threshold)[PSYoungGen: 18640K-
>0K(1835008K)] [ParOldGen: 16K->18327K(1538048K)] 18656K->18327K(3373056K), [Metaspace: 20401K-
>20398K(1069056K)], 0.0624559 secs] [Times: user=0.19 sys=0.00, real=0.06 secs]

# 第一部分
2021-05-19T14:46:07.367+0800: 1.562: [Full GC (Metadata GC Threshold)
GC事件开始时间,+0800代表中国所在的东区:
GC事件开始时间相对于JVM开始启动的间隔秒数:2.340
区分YoungGC还是FullGC的标志
触发GC的原因: (Allocation Failure)
# 第二部分
[PSYoungGen: 18640K->0K(1835008K)] [ParOldGen: 16K->18327K(1538048K)] 18656K->18327K(3373056K),
垃圾收集器的名称:PSYoungGen
在垃圾收集之前 和 之后新时代的使用量:18640K->0K
新生代总空间大小:(1835008K)
老年代垃圾收集器名称:ParOldGen
在垃圾收集之前和之后老年代的使用量:16K->18327K
老年代总空间大小:(1538048K)
在垃圾收集之前和之后整个堆内存使用量:18656K->18327K
堆总空间大小:(3373056K)
# 第三部分
[Metaspace: 20401K->20398K(1069056K)], 0.0624559 secs] [Times: user=0.19 sys=0.00, real=0.06 secs]
元空间区域垃圾收集器:Metaspace
在垃圾收集之前和之后元空间的使用量:20401K->20398K
元空间大小:(1069056K)
GC事件持续的时间:0.0624559 secs
GC线程消耗的CPU时间:user=0.19
GC过程中,操作系统调用和系统等待事件所消耗的时间:sys=0.00
应用程序暂停时间:real=0.06 secs

 

4.8 堆内存跟元空间优化

老年代的空间大小为 274MB【那些不容易消亡的老对象】

java heap:参数-Xms和-Xmx,建议扩大至3-4倍FullGC后的老年代空间占用。274 * (3-4) = (822-1096)MB ,设置heap大小为 1096MB,最好是8的整数倍;

元空间:参数-XX:MetaspaceSize=N,设置元空间大小为128MB;

新生代:参数-Xmn,建议扩大至1-1.5倍FullGC之后的老年代空间占用。274M*(1-1.5)=(274 -411)M,设置新生代大小为 411MB,最好是8的整数倍,因此改为408M;

# 调整参数,基于当前系统运行情况这是最佳配置
JAVA_OPT="${JAVA_OPT} -Xms1096m -Xmx1096m -Xmn408m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m"
JAVA_OPT="${JAVA_OPT} -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -
XX:+PrintHeapAtGC -Xloggc:${BASE_DIR}/logs/gc-best-heap-metaspace.log"

 

4.9 线程堆栈优化

对于不同版本的Java虚拟机和不同的操作系统,栈容量最小值可能会有所限制,这主要取决于操作系统内存分页大小。譬如上述方法 中的参数-Xss128k可以正常用于32位Windows系统下的JDK 6,但是如果用于64位Windows系统下的JDK 11,则会提示栈容量最小不 能低于180K,而在Linux下这个值则可能是228K,如果低于这个最小限制,HotSpot虚拟器启动时会给出如下提示:

The Java thread stack size specified is too small. Specify at least 228k

JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256。根据应用的线程所需内存大小进行调整。在相同物理内存下,减 小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成, 如果栈不是很深, 应该是256k够 用了,大的应用建议使用512k。

JAVA_OPT="${JAVA_OPT} -Xms1096m -Xmx1096m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m -Xss512k"
JAVA_OPT="${JAVA_OPT} -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -
XX:+PrintHeapAtGC -Xloggc:${BASE_DIR}/logs/gc-best-stack.log"

 

4.10 垃圾回收器优化

吞吐量优先ps+po

默认使用ps+po 垃圾回收器组合: 并行垃圾回收器组合

JAVA_OPT="${JAVA_OPT} -Xms256m -Xmx256m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m -Xss512k"
JAVA_OPT="${JAVA_OPT} -XX:+UseParallelGC -XX:+UseParallelOldGC "
JAVA_OPT="${JAVA_OPT} -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -
XX:+PrintHeapAtGC -Xloggc:${BASE_DIR}/logs/gc-ps-po.log"

响应时间优先parnew+cms

使用cms垃圾回收器,垃圾回收器组合: parNew+CMS, cms垃圾回收器在垃圾标记,垃圾清除的时候,和业务线程交叉执行,尽量 减少stw时间,因此这垃圾回收器叫做响应时间优先;

JAVA_OPT="${JAVA_OPT} -Xms256m -Xmx256m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m -Xss512k"
JAVA_OPT="${JAVA_OPT} -XX:+UseParNewGC -XX:+UseConcMarkSweepGC "
JAVA_OPT="${JAVA_OPT} -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -
XX:+PrintHeapAtGC -Xloggc:${BASE_DIR}/logs/gc-parnew-cms.log"

G1全功能

配置G1只需要简单三步即可:

  1. 第一步,开启G1垃圾收集器
  2. 第二步,设置堆的最大内存
  3. 第三步,设置最大的停顿时间
JAVA_OPT="${JAVA_OPT} -Xms256m -Xmx256m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m -Xss512k"
JAVA_OPT="${JAVA_OPT} -XX:+UseG1GC -XX:MaxGCPauseMillis=100"
JAVA_OPT="${JAVA_OPT} -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -
XX:+PrintHeapAtGC -Xloggc:${BASE_DIR}/logs/gc-g-one.log"

对于G1垃圾收集器参数设置建议:
设置为100-300之间比较合理,不要设置的太短
堆内存小于2GB,不建议使用G1

 

4.11 案例

内存溢出的定位与分析

内存溢出在实际的生产环境中经常会遇到,比如,不断的将数据写入到一个集合中,出现了死循环,读取超大的文件等等,都可能会 造成内存溢出。
如果出现了内存溢出,首先我们需要定位到发生内存溢出的环节,并且进行分析,是正常还是非正常情况,如果是正常的需求,就应 该考虑加大内存的设置,如果是非正常需求,那么就要对代码进行修改,修复这个bug。
首先,我们得先学会如何定位问题,然后再进行分析。如何定位问题呢,我们需要借助于jmap与MAT工具进行定位分析。 接下来,我们模拟内存溢出的场景。

模拟内存溢出

编写代码,向List集合中添加100万个字符串,每个字符串由1000个UUID组成。如果程序能够正常执行,最后打印ok。

package com.hero.jvm.memory;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class TestJvmOutOfMemory {
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
for (int i = 0; i < 10000000; i++) {
String str = "";
for (int j = 0; j < 1000; j++) {
str += UUID.randomUUID().toString();
}
list.add(str);
}
System.out.println("ok");
}
}




#参数如下:
-Xms8m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError

可以看到,当发生内存溢出时,会dump文件到java_pid31092.hprof

导入到MAT工具中进行分析

jvm

可以看到,有81.72%的内存由Object[]数组占有,所以比较可疑。
分析:这个可疑是正确的,因为已经有超过80%的内存都被它占有,这是非常有可能出现内存溢出的。

检测死锁

有些时候我们需要查看下jvm中的线程执行情况,比如,发现服务器的CPU的负载突然增高了、出现了死锁、死循环等,我们该如何 分析呢?
由于程序是正常运行的,没有任何的输出,从日志方面也看不出什么问题,所以就需要看下JVM的内部线程的执行情况,然后再进行 分析查找出原因。
这个时候,就需要借助于jstack命令了,jstack的作用是将正在运行的jvm的线程情况进行快照,并且打印出来:

jstack 18487 | grep 'BLOCKED' -A 15 --color

使用Arthas进行分析

thread -b

jvm

可以准确知道: 死锁在代码的中的第几行

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2203138.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LINUX 系统管理操作

基础编辑 Tab 单击一次补全 双击列举候选 CTRL U 删除光标前 K 删除光标后 L 清屏&#xff08;只剩新命令行&#xff09; C 取消当前操作 反斜杠“\” 在需要转行的时候输入反斜杠 “\”回车 在>后继续输入 帮助命令 help 命令 大部分内建命令 格式&#xff1a;h…

直播预告 | 药品安全与合规保障难?智能温度监测助您领先制药工业4.0!

您是否在为温度敏感药品的运输和存储合规而苦恼&#xff1f; 是否担心冷链物流中的温度监控漏洞导致药品质量下降&#xff1f; 制药环境中的温湿度监控是否让您无从下手&#xff1f; 这些问题不仅影响药品的安全性&#xff0c;也直接影响企业的合规性和市场竞争力。如何确保环…

Android 保存图片到相册却不在“照片”中显示,只在相簿中显示

背景 需要从网络上下载图片到本地&#xff0c; 并显示在相册中 问题 将图片保存到内存中&#xff0c; 通过媒体API插入到媒体库后&#xff0c;图片只在“相簿”中的“所有项目”中显示&#xff0c;第一个页面的“照片”却不显示 解决办法 图片被保存到 Pictures/AppName 目录…

Linux系统通过编辑crontab来设置定时任务---定时关机

在Linux系统中&#xff0c;crontab 是用来设置周期性被执行的指令的守护进程。通过编辑 crontab&#xff0c;您可以安排定时任务&#xff0c;比如定时关机、定时备份文件、定时运行脚本等。以下是如何编辑 crontab 来设置定时任务的步骤&#xff1a; 打开终端&#xff1a;您可以…

基于springboot+vue的在线宠物用品交易网站

一、系统架构 前端&#xff1a;vue | element-ui | html 后端&#xff1a;springboot | mybatis-plus 环境&#xff1a;jdk1.8 | mysql | maven | nodejs 二、代码及数据库 三、功能介绍 01. web端-首页1 02. web端-首页2 03. web端-注册 04. web端-登录 05. w…

“万万没想到”,“人工智能”获得2024年诺贝尔物理学奖

近日&#xff0c;2024年诺贝尔物理学奖颁发给了机器学习与神经网络领域的研究者&#xff0c;这是历史上首次出现这样的情况。这项奖项原本只授予对自然现象和物质的物理学研究作出重大贡献的科学家&#xff0c;如今却将全球范围内对机器学习和神经网络的研究和开发作为了一种能…

SQLite Developer使用说明

1.SQLite Developer下载 SQLite Developer官方版是SharpPlus出品的一款数据库管理工具。支持对sqlite3数据库的管理&#xff0c;能够自动完成窗口显示和执行数据库命令等多种特色。并且支持打开.db文件&#xff0c;适用于Android的开发。另外&#xff0c;使用Sqlite Developer…

压缩包格式未知或损坏怎么办?四个简单修复步骤

压缩文件是我们日常工作中常用的工具&#xff0c;但有时在解压时会遇到提示“格式未知”或“压缩包已损坏”的情况。 这可能是由于下载不完整、文件传输错误、存储介质损坏等原因导致的。这种情况会影响到我们正常获取文件&#xff0c;尤其是当压缩包内含重要数据时更让人头疼…

跨境电商独立站||代码建站和SaaS建站的区别

代码建站和SaaS建站是两种不同的网站搭建方式&#xff0c;它们各有特点和适用场景&#xff1a; 1. 代码建站&#xff1a; 定义&#xff1a;指的是从零开始&#xff0c;使用HTML、CSS、JavaScript等编程语言编写代码来构建网站的过程。 技术要求&#xff1a;需要具备一定的编程知…

vue-自定义加载界面v-loading

在网络请求中&#xff0c;页面会出现空白&#xff0c;要使页面好看点&#xff0c;通常页面会出现一些加载页面 1.准备一个伪类元素需要&#xff0c;用伪类元素给加载界面装上蒙层 .loading:before{content:;position: absolute;left:0;top:0;width: 100%;height: 100%;backgrou…

360度评估与绩效考核的深度融合,助力员工提升自我

客户背景 该零售业企业是一家集水果采购、种植支持、采后保鲜、物流仓储、标准分级、营销拓展、品牌运营、门店零售、信息科技、金融资本、科研教育于一体的大型连锁企业。 在当今快速变化的商业环境中&#xff0c;企业对于人才管理的要求日益提高&#xff0c;传统的绩效考核方…

Windows环境下CTRL+C信号处理函数的执行线程

1. 捕获CTRLC 有时候我们希望自己的程序被CTRLC以后&#xff0c;可以先执行一些收尾的工作才结束&#xff0c;比如释放动态内存&#xff0c;关闭网络端口、保存一些状态日志等等&#xff0c;可以用到C的signal的机制。 例程如下&#xff1a; #include <iostream> #inc…

前端视角下的状态机范式

状态机范式及状态管理 前言 随着产品迭代、业务量和需求量的增加&#xff0c;通常系统平台会出现代码逻辑复杂、状态混乱、维护成本增加等现象。 做同一个需求&#xff0c;最开始可能只需一天完成&#xff0c;经过长期迭代后&#xff0c;修改一个小点就可能花上两三天甚至更…

‌美国加勒比烈火波本混合威士忌新品发布会盛大举行‌

近日&#xff0c;美国知名威士忌品牌GARBBEN BLAZE加勒比烈火在北京举行了一场别开生面的新品发布会&#xff0c;正式推出了其最新力作——加勒比烈火波本混合威士忌。此次发布会吸引了众多威士忌爱好者、行业专家代表前来见证这一重要时刻。 加勒比烈火波本威士忌作为GARBBEN …

SSH 公钥认证:从gitlab clone项目repo到本地

这篇文章的分割线以下文字内容由 ChatGPT 生成&#xff08;我稍微做了一些文字上的调整和截图的补充&#xff09;&#xff0c;我review并实践后觉得内容没有什么问题&#xff0c;由此和大家分享。 假如你想通过 git clone git10.12.5.19:your_project.git 命令将 git 服务器上…

【Golang】Go语言Seeker接口与文件断点续传实战

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

kubelet PLEG 实现

概述 kubelet的主要作用是确保pod状态和podspec保持一致&#xff0c;这里的pod状态包括pod中的container状态&#xff0c;个数等。 为了达到这个目的&#xff0c;kubelet需要从多个来源watch pod spec的变化&#xff0c;并周期从container runtime获取最新的container状态。比如…

【Linux:线程锁】

目录 锁的接口&#xff1a; ​编辑 关于锁的几个问题&#xff1a; 从底层实现理解锁&#xff1a; 锁的接口&#xff1a; pthread_mutex_lock函数申请锁成功后&#xff0c;会返回&#xff0c;申请失败&#xff0c;函数不返回&#xff0c;继续申请&#xff0c;线程阻塞pthrea…

python33_数字格式化

数字格式化 数字格式输出说明3.1415926{:.2f}3.14保留小数点后两位3.1415926{:.2f}3.14带符号保留小数点后2位3.1415926{:.0f}3不带小数&#xff0c;但是会四舍五入5{:0>2d}05数字补0&#xff0c;填充左边&#xff0c;宽度为25{:x<4d}5xxx数字补x&#xff0c;填充右边&a…

IPv6 DAD和地址解析实验

实验目的 1、 观察并分析IPv6 DAD过程 2、 观察并分析IPv6地址解析过程 实验拓扑 实验步骤 1、观察并分析IPv6 DAD过程 在AR1的G0/0/0开启抓包。AR1的G0/0/0接口配置ipv6地址2012::1/64。在接口配置ipv6地址后&#xff0c;ipv6有一段称为Tentative生命期&#xff0c;在该阶…