【JVM系列7】JVM知识点总览(二)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 我理解JVM应该属于Java高阶的知识,之前是通过博客学习这块知识,后来对照着《深入理解Java虚拟机》书籍把重点知识又过了一遍。经过大半个月的学习,我给出我学习的感受。

GC日志


GC日志开启

偷个懒,直接贴网上的内容:

image.gifimage.gifQKSM{BX{8P1[R43S$%(%)9P.png

_6OQ6LPKV7B~0Q[X{0[UBMD.png

理解GC日志

Minor GC日志:image.gif

{00$@4@F7M]S9]A3IEG]WWF.png

Full GC日志:image.gif

$]`TPNCXTV)2@]{7HZF$Y$K.png

JVM常用命令


Sun JDK监控和故障处理命令有jps、jstat、jmap、jhat、jstack、jinfo

jps

JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。

jstat

jstat(JVM statistics Monitoring)是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。

jmap

dump堆到文件,可用于对文件的分析。

jhat

jhat(JVM Heap Analysis Tool)命令是与jmap搭配使用,用来分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看。在此要注意,一般不会直接在服务器上进行分析,因为jhat是一个耗时并且耗费硬件资源的过程,一般把服务器生成的dump文件复制到本地或其他机器上进行分析。

jstack

jstack用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。


性能检测工具


jconsole

Jconsole(Java Monitoring and Management Console)是从java5开始,在JDK中自带的java监控和管理控制台,用于对JVM中内存,线程和类等的监控,是一个基于JMX(java management extensions)的GUI性能监测工具。jconsole使用jvm的扩展机制获取并展示虚拟机中运行的应用程序的性能和资源消耗等信息。

概览:包括堆内存使用情况、线程、类、CPU使用情况四项信息的曲线图。

0_LSNU(}8]%~GQ0WZBW$65D.jpgimage.gif

线程:相当于可视化的jstack命令,同时也可以点击“检测死锁”来检查线程之间是否有死锁的情况。

image.gifI7Q2)5B)3G2}14%YA3PZ2KA.png

VisualVM

VisualVM(All-in-One Java Troubleshooting Tool)是功能最强大的运行监视和故障处理程序之一,曾经在很长一段时间内是Oracle官方主力发展的虚拟机故障处理工具。

相比一些第三方工具,VisualVM有一个很大的优点:不需要被监视的程序基于特殊Agent去运行,因此它的通用性很强,对应用程序实际性能的影响也较小,使得它可以直接应用在生产环境中。

Visual GC 是常常使用的一个功能,需要通过插件按照,可以明显的看到年轻代、老年代的内存变化,以及gc频率、gc的时间等,感觉这个插件很酷!

KZE$3}IU[C`7~C4JGQM6ZOC.png

监控的主页其实也就是,cpu、内存、类、线程的图表,这里面可以执行堆dump。

%XQ@@VA9J3SX$`G81QY(3O7.png

最后就是堆dump:

image.gifWI{XHZA2YQEA@2ZP_6PTR2P.jpg


JVM调优


选择合适的垃圾回收器

  • CPU单核,那么毫无疑问Serial 垃圾收集器是你唯一的选择。
  • CPU多核,关注吞吐量 ,那么选择PS+PO组合。
  • CPU多核,关注用户停顿时间,JDK版本1.6或者1.7,那么选择CMS。
  • CPU多核,关注用户停顿时间,JDK1.8及以上,JVM可用内存6G以上,那么选择G1。

参数配置:

//设置Serial垃圾收集器(新生代)
 开启:-XX:+UseSerialGC
 //设置PS+PO,新生代使用功能Parallel Scavenge 老年代将会使用Parallel Old收集器
 开启 -XX:+UseParallelOldGC
 //CMS垃圾收集器(老年代)
 开启 -XX:+UseConcMarkSweepGC
 //设置G1垃圾收集器
 开启 -XX:+UseG1GC


调整内存大小

现象:垃圾收集频率非常频繁。

原因:如果内存太小,就会导致频繁的需要进行垃圾收集才能释放出足够的空间来创建新的对象,所以增加堆内存大小的效果是非常显而易见的。

注意:如果垃圾收集次数非常频繁,但是每次能回收的对象非常少,那么这个时候并非内存太小,而可能是内存泄露导致对象无法回收,从而造成频繁GC。

参数配置:

//设置堆初始值
 指令1:-Xms2g
 指令2:-XX:InitialHeapSize=2048m
 //设置堆区最大值
 指令1:`-Xmx2g` 
 指令2: -XX:MaxHeapSize=2048m
 //新生代内存配置
 指令1:-Xmn512m
 指令2:-XX:MaxNewSize=512m


设置符合预期的停顿时间

现象:程序间接性的卡顿

原因:如果没有确切的停顿时间设定,垃圾收集器以吞吐量为主,那么垃圾收集时间就会不稳定。

注意:不要设置不切实际的停顿时间,单次时间越短也意味着需要更多的GC次数才能回收完原有数量的垃圾.

参数配置:

//GC停顿时间,垃圾收集器会尝试用各种手段达到这个时间
 -XX:MaxGCPauseMillis


调整内存区域大小比率

现象:某一个区域的GC频繁,其他都正常。

原因:如果对应区域空间不足,导致需要频繁GC来释放空间,在JVM堆内存无法增加的情况下,可以调整对应区域的大小比率。

注意:也许并非空间不足,而是因为内存泄造成内存无法回收。从而导致GC频繁。

参数配置:

//survivor区和Eden区大小比率
 指令:-XX:SurvivorRatio=6  //S区和Eden区占新生代比率为1:6,两个S区2:6
 //新生代和老年代的占比
 -XX:NewRatio=4  //表示新生代:老年代 = 1:4 即老年代占整个堆的4/5;默认值=2


调整对象老年代的年龄

现象:老年代频繁GC,每次回收的对象很多。

原因:如果升代年龄小,新生代的对象很快就进入老年代了,导致老年代对象变多,而这些对象其实在随后的很短时间内就可以回收,这时候可以调整对象的升级代年龄,让对象不那么容易进入老年代解决老年代空间不足频繁GC问题。

注意:增加了年龄之后,这些对象在新生代的时间会变长可能导致新生代的GC频率增加,并且频繁复制这些对象新生的GC时间也可能变长。

配置参数:

//进入老年代最小的GC年龄,年轻代对象转换为老年代对象最小年龄值,默认值7
 -XX:InitialTenuringThreshol=7


调整大对象的标准

现象:老年代频繁GC,每次回收的对象很多,而且单个对象的体积都比较大。

原因:如果大量的大对象直接分配到老年代,导致老年代容易被填满而造成频繁GC,可设置对象直接进入老年代的标准。

注意:这些大对象进入新生代后可能会使新生代的GC频率和时间增加。

配置参数:

//新生代可容纳的最大对象,大于则直接会分配到老年代,0代表没有限制。
  -XX:PretenureSizeThreshold=1000000


调整GC的触发时机

现象:CMS,G1 经常 Full GC,程序卡顿严重。

原因:G1和CMS  部分GC阶段是并发进行的,业务线程和垃圾收集线程一起工作,也就说明垃圾收集的过程中业务线程会生成新的对象,所以在GC的时候需要预留一部分内存空间来容纳新产生的对象,如果这个时候内存空间不足以容纳新产生的对象,那么JVM就会停止并发收集暂停所有业务线程(STW)来保证垃圾收集的正常运行。这个时候可以调整GC触发的时机(比如在老年代占用60%就触发GC),这样就可以预留足够的空间来让业务线程创建的对象有足够的空间分配。

注意:提早触发GC会增加老年代GC的频率。

配置参数:

//使用多少比例的老年代后开始CMS收集,默认是68%,如果频繁发生SerialOld卡顿,应该调小
 -XX:CMSInitiatingOccupancyFraction
 //G1混合垃圾回收周期中要包括的旧区域设置占用率阈值。默认占用率为 65%
 -XX:G1MixedGCLiveThresholdPercent=65


调整 JVM本地内存大小

现象:GC的次数、时间和回收的对象都正常,堆内存空间充足,但是报OOM

原因:JVM除了堆内存之外还有一块堆外内存,这片内存也叫本地内存,可是这块内存区域不足了并不会主动触发GC,只有在堆内存区域触发的时候顺带会把本地内存回收了,而一旦本地内存分配不足就会直接报OOM异常。

注意:本地内存异常的时候除了上面的现象之外,异常信息可能是OutOfMemoryError:Direct buffer memory。解决方式除了调整本地内存大小之外,也可以在出现此异常时进行捕获,手动触发GC(System.gc())。

配置参数:

XX:MaxDirectMemorySize


JVM调试实战


网站流量浏览量暴增后,网站反应页面很慢

1、问题推测:在测试环境测速度比较快,但是一到生产就变慢,所以推测可能是因为垃圾收集导致的业务线程停顿。

2、定位:为了确认推测的正确性,在线上通过jstat -gc 指令 看到JVM进行GC 次数频率非常高,GC所占用的时间非常长,所以基本推断就是因为GC频率非常高,所以导致业务线程经常停顿,从而造成网页反应很慢。

3、解决方案:因为网页访问量很高,所以对象创建速度非常快,导致堆内存容易填满从而频繁GC,所以这里问题在于新生代内存太小,所以这里可以增加JVM内存就行了,所以初步从原来的2G内存增加到16G内存。

4、第二个问题:增加内存后的确平常的请求比较快了,但是又出现了另外一个问题,就是不定期的会间断性的卡顿,而且单次卡顿的时间要比之前要长很多。

5、问题推测:练习到是之前的优化加大了内存,所以推测可能是因为内存加大了,从而导致单次GC的时间变长从而导致间接性的卡顿。

6、定位:还是通过jstat -gc 指令 查看到 的确FGC次数并不是很高,但是花费在FGC上的时间是非常高的,根据GC日志 查看到单次FGC的时间有达到几十秒的。

7、解决方案: 因为JVM默认使用的是PS+PO的组合,PS+PO垃圾标记和收集阶段都是STW,所以内存加大了之后,需要进行垃圾回收的时间就变长了,所以这里要想避免单次GC时间过长,所以需要更换并发类的收集器,因为当前的JDK版本为1.7,所以最后选择CMS垃圾收集器,根据之前垃圾收集情况设置了一个预期的停顿的时间,上线后网站再也没有了卡顿问题。


后台导出数据引发的OOM

**问题描述:**公司的后台系统,偶发性的引发OOM异常,堆内存溢出。

1、因为是偶发性的,所以第一次简单的认为就是堆内存不足导致,所以单方面的加大了堆内存从4G调整到8G。

2、但是问题依然没有解决,只能从堆内存信息下手,通过开启了-XX:+HeapDumpOnOutOfMemoryError参数 获得堆内存的dump文件。

3、VisualVM 对  堆dump文件进行分析,通过VisualVM查看到占用内存最大的对象是String对象,本来想跟踪着String对象找到其引用的地方,但dump文件太大,跟踪进去的时候总是卡死,而String对象占用比较多也比较正常,最开始也没有认定就是这里的问题,于是就从线程信息里面找突破点。

4、通过线程进行分析,先找到了几个正在运行的业务线程,然后逐一跟进业务线程看了下代码,发现有个引起我注意的方法,导出订单信息。

5、因为订单信息导出这个方法可能会有几万的数据量,首先要从数据库里面查询出来订单信息,然后把订单信息生成excel,这个过程会产生大量的String对象。

6、为了验证自己的猜想,于是准备登录后台去测试下,结果在测试的过程中发现到处订单的按钮前端居然没有做点击后按钮置灰交互事件,结果按钮可以一直点,因为导出订单数据本来就非常慢,使用的人员可能发现点击后很久后页面都没反应,结果就一直点,结果就大量的请求进入到后台,堆内存产生了大量的订单对象和EXCEL对象,而且方法执行非常慢,导致这一段时间内这些对象都无法被回收,所以最终导致内存溢出。

7、知道了问题就容易解决了,最终没有调整任何JVM参数,只是在前端的导出订单按钮上加上了置灰状态,等后端响应之后按钮才可以进行点击,然后减少了查询订单信息的非必要字段来减少生成对象的体积,然后问题就解决了。

相关文章
|
27天前
|
存储 缓存 算法
JVM核心知识点整理(内存模型),收藏再看!
JVM核心知识点整理(内存模型),收藏再看!
JVM核心知识点整理(内存模型),收藏再看!
|
4月前
|
存储 算法 安全
Java面试题:Java内存模型及相关知识点深度解析,Java虚拟机的内存结构及各部分作用,详解Java的垃圾回收机制,谈谈你对Java内存溢出(OutOfMemoryError)的理解?
Java面试题:Java内存模型及相关知识点深度解析,Java虚拟机的内存结构及各部分作用,详解Java的垃圾回收机制,谈谈你对Java内存溢出(OutOfMemoryError)的理解?
68 0
|
存储 算法 Java
Java虚拟机相关知识点1
Java虚拟机相关知识点
|
存储 缓存 监控
JVM关键知识点整理,从入门到提高到实践
Java 虚拟机定义了各种在程序执行期间使用的运行时数据区域。这些数据区域有一些是在Java虚拟机启动时创建的,并在Java虚拟机退出时销毁,有一些数据区域是每个线程独有的,在线程创建时创建,在线程销毁时销毁,根据《Java虚拟机规范》的规定,Java虚拟机运行时所需要管理的数据区域主要如下图所示:
339 0
JVM关键知识点整理,从入门到提高到实践
|
缓存 算法 安全
JVM知识点总结
JVM知识点总结
70 0
|
存储 缓存 算法
Java虚拟机相关知识点2
Java虚拟机相关知识点
|
存储 缓存 架构师
京东架构师呕心整理:jvm与性能调优有哪些核心技术知识点
相信很多人对于性能调优都不陌生,为了获得更好的系统性能,或者是为了满足不断增加的业务需求。都需要用到我们的性能调优。所以性能优化在面试中出现的频率特别高,这篇文章我主要给大家整理了大厂里面关于jvm和性能调优用到的一些核心技术知识点。
81 0
|
算法 Java 程序员
阿里P8大牛精心整理JVM性能优化知识点+最新JVM面试题(附答案)
JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。它不仅是一种跨平台的软件,而且是一种新的网络计算平台。该平台包括许多相关的技术,如符合开放接口标准的各种API、优化技术等。
|
存储 缓存 Java
JVM内存结构详细总结(结合自身使用经历介绍各个知识点使用场景)
如图JVM运行时数据区域划分为以下6个主要部分:①程序计数器,②虚拟机栈,③本地方法栈,④虚拟机堆,⑤方法区,⑥直接内存,下面对6个部分详细总结,希望可以对路过的朋友有所帮助。
124 0
JVM内存结构详细总结(结合自身使用经历介绍各个知识点使用场景)
|
存储 算法 Java
【Java】总结一下JVM知识点
总结一下JVM知识点
107 0
【Java】总结一下JVM知识点