jvm由浅入深

2020-08-14  本文已影响0人  ajajaj

JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。以上是百度对于jvm的介绍

jvm的组成

jvm主要由类加载器、运行时数据区、执行引擎,三部分组成。整体结构如下

<img src="https://gitee.com/cbaj/blogImag/raw/master/img/20200813143159.png" width="400" height="400" align="middle" />

运行时数据区主要包括:方法区、栈、堆、程序计数器,下面详细介绍下各个模块

方法区

java虚拟机栈

结合伪代码、进出栈图理解下栈

a(){
    b(); 
   }
b(){
    c(); 
   }
c(){ 
}
image

程序计数器

本地方法栈

如果当前线程执行的方法是Native类型的,这些方法就会在本地方法栈中执行。

程序运行过程

1、jvm启动后,会根据参数到指定目录下加载.class的类文件,类文件被加载到内存后,放在方法区

2、jvm创建一个主线程执行main方法,main方法中的参数方法内定义的变量被压栈到“java虚拟机栈

3、如果方法内存在实例,实例信息被存放在中,这个对象实例的引用存放在“java虚拟机栈”中

4、方法区中存放着类中可以执行的代码,而方法内的局部变量又存放在

思考一下

栈指向堆

这个比较好理解,如果在栈帧中有一个变量,类型为引用类型,比如Object obj=new Object(),这时候就是典型的栈中元 素指向堆中的对象。

方法区指向堆

方法区中会存放静态变量,常量等数据。如果是下面这种情况,就是典型的方法区中元素指向堆中的对象。

private static Object obj=new Object();

堆指向方法区

方法区中会包含类的信息,堆中会有对象,那怎么知道对象是哪个类创建的呢?

java对象内部布局

一个Java对象在内存中包括3个部分:对象头、实例数据和对齐填充

image

内存模型

image

1、大多数情况下对象分配在Eden中,如果Eden空间不足时,虚拟机会发起一次Minor Gc

2、新生代Gc(Minor Gc):在新生代发生的垃圾回收动作,因为java对象大多数有朝生夕灭的规律,所以Minor Gc发生的非常频繁,而且速度也很快

3.老年代Gc(Major Gc(清理老年代)、Full Gc( 是清理整个堆空间—包括年轻代和老年代)):Full Gc 往往会伴随着一次Minor Gc,当Minor Gc产生时会判断 a.老年代最大可用的连续空间是否大于新手代所有对象所占的空间 成立 进行Minor Gc b.a不成立 查看虚拟机参数是否允许担保失败 如果不允许Full Gc 如果允许,继续检查老年代最大连续可用空间是否大于历次晋升到老年代对象的平均大小 c.b成立 进行Minor Gc 如果不成立进行Full Gc

补充

1、大部分对象创建都是在Eden的,除了个别大对象外。

2、Minor GC开始前,to-survivor是空的,from-survivor是有对象的。

3、Minor GC后,Eden的存活对象都copy到to-survivor中,from-survivor的存活对象也复制to-survivor中。其中所有对象的年龄+1

4、from-survivor清空,成为新的to-survivor,带有对象的to-survivor变成新的from-survivor。重复回到步骤2

image

jvm的垃圾回收

通过可达性算法分析,通过一系列的GC root的节点作为起始节点向下查找,搜索所经过的路径被称为"引用链",当一个对象到Gc root没经过任何引用链则证明此对象是不可用的

通过可达性算法存在的问题:逐个检查比较耗时,在可达性分析的时候必须保证一致性,停掉所有执行的java线程。即使是在号称(几乎)不会发生停顿的CMS收集器中,枚举根节点时也是必须要停顿的。

在java中,可作为GC Roots的对象有:

标记-清除算法

标记-清除算法原理简单,但是效率不高、导致回收后的空间不连续,如果放入较大对象时,还需要进行另一次垃圾回收

复制算法

将内存分为两块,每次只是用其中的一块,这一块快用完的时候,把存活的对象复制到另外一块,然后把一用完的空间回收掉。

标记-整理算法

和标记清理算法类似,只是后面不是直接回收,而是将存活的对象移到一端,然后进行回收边界以外的区域(老年代)

分代收集算法

一般把java堆分为新生代、老年代,新生代存活的对象较少使用复制算法,老年代使用复制算法、标记-清除算法

垃圾回收器

新生代

serial

单线程收集器,每次执行来及回收时需要停掉其他所有的工作线程,直到它收集完成。(默认的新生代回收器,简单高效)

parNew:

是serial多线程版本

Parallel Scavenge

精确的控制吞吐量(应用场景:高吞吐量为目标,减少垃圾回收的时间),有一个UserAdaptiveSizePolicy开关参数,这个参数打开后,不需要手动指定jvm各个空间的大小,会自动分析性能调节大小

老年代

serial old

是serial的老年代版本,针对老年代,采用标记整理算法

parallel old

是parallel的老年代版本,采用标记整理算法

CMS

基于标记:清除算法(产生碎片),并发收集,停顿时间短 (初始化标记:标记Gc roots直接关联到的对象
并发标记:进行Gc Roots Tracing的过程 重新标记:修正之前标记后程序运行发生变动的对象 并发清除:初始化标记和重新标记任然需要stop the world,整个过程最耗时的并发标记、并发清理都可以和用户线程同步进行,所以CMS是并发收集,低停顿,一款优秀的收集器 缺点

整堆回收G1:并发与并行,可以充分利用多cpu,可以独立管理整个Gc堆,不需要与其他回收器组合使用。对于整个堆空间没有明显的物理界限,而是划分成多个region,G1可以建立一个可预测的停顿时间模型,跟踪各个Region里面垃圾堆积价值大小,自动维护一个优先列表,根据优先级优先回收价值最大的region,保证了再有限的时间内获得尽可能高的收集效率(化整为零的思路)。

G1化整为零的思想看着简单,但从sun实验室发表G1论文开始,近是10年才开发出G1的商用版。以一个细节为例堆分成多个region后,能否真的简单的以region为单位进行回收,很多时候会存在一个对象存在于a-region但引用关系可能存在于b-region,甚至于可以和整个堆上的任意对象发生关系,那么在回收的时候岂不是要扫描整个堆,这个问题不仅仅在G1中存在。虚拟机是使用 Remembered Set来避免全堆扫描G1每一个region对于Remembered Set,会将对象引用关系存在Remembered Set,所以可以避免全队扫描又不会遗漏。(初始标记、并发标记、最终标记、筛选回收)

原文地址

http://cbaj.gitee.io/blog/2020/08/13/jvm%E7%94%B1%E6%B5%85%E5%85%A5%E6%B7%B1/#more

上一篇下一篇

猜你喜欢

热点阅读