JVM相关知识讲解

在这里插入图片描述

一、什么是JVM

JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。

在这里插入图片描述

二、Java代码执行流程

在这里插入图片描述

三、JVM的组成

在这里插入图片描述

类加载子系统
  • 类加载子系统负责从文件系统加载Class文件,Class文件在文件开头有特定的文件标识。
  • ClassLoader只负责class文件的加载,至于它是否可运行,则由执行引擎决定。
  • 加载的类信息存放于一块称为方法区的内存空间。除了类的信息外,方法区中还会存放运行时常量池信息,还包括字符串字面量和数字常量(这部分常量信息是Class文件中常量池部分的内存映射)
运行时数据区

虚拟机内存或者Jvm内存,在整个计算机内存中开辟一块内存存储Jvm需要用到的对象,变量等,运行时数据区又分很多小的区域,分别为:程序计数器、虚拟机栈、本地方法栈、堆、方法区。JVM调优主要就是优化Heap堆 和 Method Area 方法区。

程序计数器
  • 它是一块很小的内存空间,几乎可以忽略不计。也是运行速度最快的存储区域。
  • 在JVM规范中,每个线程都有它自己的程序计数器,是线程私有的,生命周期与线程的生命周期一致。
  • 任何时间一个线程都只有一个方法在执行,也就是所谓的当前方法。程序计数器会存储当前线程正在执行的Java方法的JVM指令地址。
虚拟机栈

在这里插入图片描述
每个线程创建的同时会创建一个JVM栈,JVM栈中每个栈帧存放的为当前线程中局部基本类型的变量、部分的返回结果,非基本类型的对象在JVM栈上仅存放一个指向堆上的地址;每一个方法从被调用直至执行完成的过程就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。栈运行原理:栈中的数据都是以栈帧的格式存在,栈帧是一个内存区块,是一个数据集,是一个有关方法和运行期数据的数据集,当一个方法A被调用时就产生了一个栈帧1,并被压入到栈中,A方法又调用了B方法,于是产生栈帧2也被压入栈,B方法又调用了C方法,于是产生栈帧3也被压入栈…栈帧n也被压入栈依次执行完毕后,先弹出后进的栈帧n…再弹出栈帧3,再弹出栈帧2,再弹出栈帧1。JAVA虚拟机栈的最小单位可以理解为一个个栈帧,一个方法对应一个栈帧,一个栈帧可以执行很多指令。
在这里插入图片描述

方法区
  • 方法区和堆一样,是各个线程共享的内存区域。
  • 方法区在JVM启动的时候被创建,并且它的实际物理内存空间中和JVM堆区一样都可以是不连续的。
  • 方法区的大小,跟堆空间一样,可以选择固定大小或者可扩展。
  • 方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出内存溢出错误。
  • 关闭JVM就会释放这个区域的内存。
  • 一个JVM实例只存在一个堆内存,堆也是Java内存管理的核心区域。
  • 堆区在JVM启动的时候即被创建,其空间大小也就确定了。是JVM管理的最大的一块内存空间。
  • 堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的。
  • 所有线程共享堆区,在这里还可以划分线程私有的缓冲区(TLAB)。
执行引擎
  • 执行引擎是Java虚拟机核心的组成部分之一。
  • 任务就是将字节码指令解释/编译为对应平台上的本地机器指令。
本地方法
  • 本地方法接口就是Java调用非Java代码的接口,其作用是融合不同的编程语言为Java所用,它的初衷是融合C/C++程序。
  • 目前该方法使用的越来越少了,除非是与硬件有关的应用。
  • Java虚拟机栈用于管理Java方法的调用,而本地方法栈用于管理本地方法的调用。
  • 本地方法栈也是线程私有的。
  • 具体做法是本地方法栈中登记Native方法,在执行引擎执行时加载本地方法库。
总结

在这里插入图片描述

四、JVM垃圾回收

什么是垃圾回收机制

垃圾回收(Garbage Collection,GC),顾名思义就是释放垃圾占用的空间,防止内存泄露。垃圾回收能有效的利用可以使用的内存,对内存中已经没有任何引用的对象进行清除和内存回收。

了解堆内存

先看下JVM堆内存是如何划分的,如图:
在这里插入图片描述

  • JVM内存划分为堆内存和非堆内存,堆内存分为年轻代(Young Generation)、老年代(Old Generation),非堆内存就一个永久代(Permanent Generation)或元空间(MetaSpace)。
  • 年轻代又分为Eden和Survivor区。Survivor区由FromSpace和ToSpace组成。Eden区占大容量,Survivor两个区占小容量,默认比例是8:1:1。
  • 堆内存用途:存放的是对象,垃圾收集器就是收集这些对象,然后根据GC算法回收。
  • 非堆内存用途:永久代,也称为方法区,存储程序运行时需要的一些数据,比如类的元数据、方法、常量、属性等。

注:在JDK1.8版本废弃了永久代,替代的是元空间(MetaSpace),元空间与永久代上类似,都是方法区的实现,他们最大区别是:元空间并不在JVM中,而是使用本地内存。

参数描述
-Xms堆内存初始分配大小 (单位m、g )
-Xmx堆内存最大允许大小
-Xns年轻代内存初始大小
-Xmn年轻代内存最大允许大小
-Xss每个线程的堆栈内存大小
-XX:MetaspaceSize初始元空间大小
-XX:MaxMetaspaceSize最大元空间大小
-XX:SurvivorRatio=8年轻代中Eden区与Survivor区的容量比例值,默认为8,即8:1:1
-XX:NewRatio=3年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3
-XX:+PrintGC输出GC日志
-XX:+PrintGCDetails输出GC的详细日志
-XX:+PrintGCTimeStamps打印GC发生的时间戳
-Xloggc日志文件的输出路径
-XX:+HeapDumpOnOutOfMemoryError当JVM发生OOM时,自动生成DUMP文件
-XX:HeapDumpPath生成DUMP文件的路径
-XX:PreternureSizeThreshold直接晋升老年代的对象大小
-XX:MaxTenuringThreshold晋升老年代的对象年龄,默认值15
-XX:+PrintCommandLineFlags查看命令行相关参数
垃圾回收算法

注:红色是标记的非活动对象,绿色是活动对象。

  • 标记-清除(Mark-Sweep)

GC分为两个阶段,标记和清除。首先标记所有可回收的对象,在标记完成后统一回收所有被标记的对象。同时会产生不连续的内存碎片。碎片过多会导致以后程序运行时需要分配较大对象时,无法找到足够的连续内存,而不得已再次触发GC。

在这里插入图片描述

  • 复制(Copy)

将内存按容量划分为两块,每次只使用其中一块。当这一块内存用完了,就将存活的对象复制到另一块上,然后再把已使用的内存空间一次清理掉。这样使得每次都是对半个内存区回收,也不用考虑内存碎片问题,简单高效。缺点需要两倍的内存空间。

在这里插入图片描述

  • 标记-整理(Mark-Compact)

也分为两个阶段,首先标记可回收的对象,再将存活的对象都向一端移动,然后清理掉边界以外的内存。此方法避免标记-清除算法的碎片问题,同时也避免了复制算法的空间问题。
一般年轻代中执行GC后,会有少量的对象存活,就会选用复制算法,只要付出少量的存活对象复制成本就可以完成收集。而老年代中因为对象存活率高,没有额外过多内存空间分配,就需要使用标记-清理或者标记-整理算法来进行回收。

在这里插入图片描述

垃圾收集器

在这里插入图片描述

串行收集器
  • Serial垃圾收集器

最基本、历史最悠久的垃圾回收器,单线程。收集时,必须暂停应用的工作线程,直到收集结束。

在这里插入图片描述

参数描述
-XX:+UseSerialGC使用串行回收器进行回收
并行收集器
  • ParNew垃圾收集器
    在这里插入图片描述

多条垃圾收集线程并行工作,在多核CPU下效率更高,应用线程仍然处于等待状态。

  • Parallel Scavenge垃圾收集器
    在这里插入图片描述

吞吐量优先的垃圾收集器。

参数描述
-XX:+UseParNewGC新生代进行并行回收,老年代仍旧使用串行回收
-XX:+UseParallelGC新生代使用Parallel收集器,老年代使用串行收集器
-XX:MaxGCPauseMillis最大停顿时间(配合ParallelGC)
-XX:GCTimeRatio控制垃圾回收时间占比(配合ParallelGC)
-XX:UseAdaptiveSizePolicy根据实际运行情况动态调整一些细节参数(配合ParallelGC)
-XX:+UseParallelOldGC新生代和老年代都使用并行收集器
CMS收集器(Concurrent Mark Sweep)

在这里插入图片描述

CMS收集器是缩短暂停应用时间为目标而设计的,是基于标记-清除算法实现,整个过程分为4个步骤,包括:
初始标记(Initial Mark)
并发标记(Concurrent Mark)
重新标记(Remark)
并发清除(Concurrent Sweep)
其中,初始标记、重新标记这两个步骤仍然需要暂停应用线程。初始标记只是标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段是标记可回收对象,而重新标记阶段则是为了修正并发标记期间因用户程序继续运作导致标记产生变动的那一部分对象的标记记录,这个阶段暂停时间比初始标记阶段稍长一点,但远比并发标记时间短。由于整个过程中消耗最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作,所以,CMS收集器内存回收与用户一起并发执行的,大大减少了暂停时间。

参数描述
-XX:+UseConcMarkSweepGC新生代使用ParNew收集器,老年代使用CMS收集器
-XX:+ UseCMSCompactAtFullCollectionFull GC后,进行一次整理
-XX:ParallelCMSThreads并行GC时进行内存回收的线程数量
-XX:ConcGCThreads并发CMS过程运行时的线程数
G1收集器(Garbage First)

在这里插入图片描述

G1收集器将堆内存划分多个大小相等的独立区域(Region),并且能预测暂停时间,能预测原因它能避免对整个堆进行全区收集。G1跟踪各个Region里的垃圾堆积价值大小(所获得空间大小以及回收所需时间),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region,从而保证了再有限时间内获得更高的收集效率。
G1收集器工作工程分为4个步骤,包括:
初始标记(Initial Mark)
并发标记(Concurrent Mark)
最终标记(Final Mark)
筛选回收(Live Data Counting and Evacuation)
初始标记与CMS一样,标记一下GC Roots能直接关联到的对象。并发标记从GC Root开始标记存活对象,这个阶段耗时比较长,但也可以与应用线程并发执行。而最终标记也是为了修正在并发标记期间因用户程序继续运作而导致标记产生变化的那一部分标记记录。最后在筛选回收阶段对各个Region回收价值和成本进行排序,根据用户所期望的GC暂停时间来执行回收。

参数描述
-XX:+UseG1GC使用 G1 垃圾收集器
-XX:MaxGCPauseMillis期望达到的最大GC停顿时间指标(JVM会尽力实现,但不保证达到)
XX:MaxTenuringThreshold提升年老代的最大临界值(tenuring threshold),默认值为15
-XX:ParallelGCThreads垃圾收集器在并行阶段使用的线程数,默认值随JVM运行的平台不同而不同
-XX:ConcGCThreads并发垃圾收集器使用的线程数量,默认值随JVM运行的平台不同而不同
-XX:G1ReservePercent设置堆内存保留为假天花板的总量,以降低提升失败的可能性,默认值是 10
-XX:G1HeapRegionSize使用G1时Java堆会被分为大小统一的的区(region)。此参数可以指定每个heap区的大小,默认值将根据 heap size 算出最优解. 最小值为1Mb, 最大值为 32Mb
XX:InitiatingHeapOccupancyPercent启动并发GC周期时的堆内存占用百分比,默认值为 45

五、JVM常用监控工具

jinfo命令

jinfo全称Java Configuration Info,主要作用是查看JVM配置参数
用法:jinfo -flag <name> PID

直接输入jinfo PID会打印一些java系统属性
在这里插入图片描述
在最后面还会打印相关的jvm参数信息
在这里插入图片描述

单独查看某个jvm参数
jinfo -flag 相关JVM参数 PID
在这里插入图片描述

jps命令

查看当前运行的java进程

用法很简单 直接输入jps或jps -l
在这里插入图片描述

jmap命令

用法一:打印某个进程的jvm堆信息
jmap -heap PID
在这里插入图片描述

用法二:查看堆内存(histogram)中的对象数量,大小 一般查看数量排名靠前的对象
jmap -histo PID
在这里插入图片描述
用法三:导出堆内存映像
jmap -dump:format=b,file=heap.hprof PID

jstat命令

jstat用的最多的是用来统计gc相关信息

用法:jstat -gcutil PID 间隔秒数
在这里插入图片描述
S0: Survivor 0区的空间使用率
S1: Survivor 1区的空间使用率
E: Eden区的空间使用率
O: 老年代的空间使用率
M: 元数据的空间使用率
CCS: 类指针压缩空间使用率
YGC: 新生代GC次数
YGCT: 新生代GC总时长
FGC: Full GC次数
FGCT: Full GC总时长
GCT: 总共的GC时长

jstack命令

jstack主要的用途是打印出Thread dump,用来查看线程是否出现死锁

用法:jstack PID > stack.log
查找死锁可以在dump出的文件里直接搜索BLOCKED,deadlock等关键字
在这里插入图片描述
在这里插入图片描述

jvisualvm程序

jvisualvm能够监控内存泄露,跟踪垃圾回收,执行时内存、cpu分析,线程分析等,它是一款图形化界面工具,JDK中自带(1.6版本以上),直接运行即可。

查看线程信息
在这里插入图片描述
实时监控堆内存情况
在这里插入图片描述
监视程序运行时的各项指标
在这里插入图片描述

arthas工具

Arthas是Alibaba开源的Java诊断工具,功能非常强大。arthas工具下载地址:https://alibaba.github.io/arthas/arthas-boot.jar

用法:java -jar arthas-boot.jar PID
在这里插入图片描述

工具内部命令集合
在这里插入图片描述

dashboard命令展示
在这里插入图片描述

结束

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值