白话理解JVM工作原理

写在前面:基于个人的知识对jvm进行白话理解,有不对的地方欢迎留言讨论。

白话理解JVM的基本概念

一、JVM知识点的3条主线

    1.JVM的内存模型

    2.JVM的类加载机制

    3.JVM的垃圾回收机制

3条主线都是围绕JVM的内存模型展开,我们把JVM的内存模型和数据库进行横向对比来理解基本概念。

 

二、JVM内存模型图

ada7da0ec97c80ac6ed769912d3b22bb5c1.jpg

基本内存模型

72d2a144be6a13aa052c0ee66dd69b94e9b.jpg

核心内存模型

 

(一)JVM内存模型重点

  • JVM内存模型的6个区域,主流说法中常常提到的是5个区域:
  1. 方法区:用来存放class文件的数据结构区域
  2. 常量池:用来存放基本类型变量的数据区域
  3. :用来存放对象实例的数据区域
  4. :线程工作私有的数据区域
  5. 本地方法栈:调用操作系统的native方法时用到的内存区域
  6. 程序计数器:是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器
  • 基于JVM的内存模型空间分类:
  1. 公共内存空间:方法区和堆
  2. 私有内存空间:栈、本地方法栈、程序计数器
  3. 另类空间,常量池

 

(二)白话理解概念

我们尝试通过与数据库横向类比了解JVM的内存模型中4个最主要的区域,

  1. 方法区:方法区类似数据库的元数据表,我们可以认为方法区就是数据库用来存储表名字、字段、索引等信息的元数据映射库表。方法区主要的作用是JVM载入class文件后构建的对应内存模型,比如说一个class文件有版本信息、基本类型、类的各种接口和方法描述信息,常量定义等,所以一个class文件需要在内存有数据结构映射,这个用来存储class文件的数据结构映射的空间就是方法区,每一个类有一个映射记录。
  2. 堆:堆类似与数据库的业务表空间,我们可以认为堆里面每创建一个对象就是新建立了一张业务表,同一个对象的不同实例类似于业务表中的数据记录。JVM每次new出来的对象都是放在堆中,是JVM最重要的一个区域。
  3. 栈:栈类似与数据库中的temp临时表。JVM中启动任何一个线程运行需要分配内存空间,这个空间就是栈,所以栈空间是线程私有的。
  4. 另类空间常量池栈:

为什么说常量池是另类空间呢,因为JAVA是面向对象的,理论上处理的所有事情都是类对象。然后Java语言在真实设计上需要处理的数据有两类,1.基本类型,2.java定义的类,基于这个设计要求,所以jvm也做了相应的优化处理

1.如果是基本类型的数据,保存在常量池中。基本类型对应的如String、int、long、double、float、short、byte、boolean、char

2.如果是类对应的实例对象引用,保存在堆中.

这个常量池还有一个另类的地方,就是jdk1.6是定义在方法区的,jdk1.7、1.8以后是定义在堆空间的,为什么会这样的,这是因为在jvm加载class文件处理常量的时候就用到了常量池,所以当时认为他们是一起的,其实我们真实分析方法区的核心功能就是用来映射class文件到内存的数据模型,就是用来存储元数据的;而堆就是用来存全局变量的,无论他是基本类型变量还是java的对象实例,这样的定位更清晰。

 

三、JVM的类加载机制

a43be33e12ce7647c05e7d459de1b604184.jpg

类加载过程图

d729f859c006eff234059e637531406be98.jpg

虚拟机类加载双亲委派顺序图

(一)JVM类加载机制重点

  • JVM的类加载机制的2个概念:
  1. JVM的类加载的7个过程,加载、准备、验证、解析、初始化、使用、卸载
  2. JVM的双亲委派模型:

  BootstrapClassLoader启动类加载器:加载系统环境变量下JAVA_HOME/lib目录下的类库。

  ExtClassLoader扩展类加载器:加载JAVA_HOME/lib/ext目录下的类库。

  AppClassLoader应用程序类加载器(系统类加载器):加载用户类路径Class_Path指定的类库。

  自定义类加载器:如果需要自定义加载时的规则(比如:指定类的字节流来源、动态加载时性能优化等),可以自己实现类加载器。

(二)白话理解概念

  • 类加载:

我们依然尝试通过与数据库横向类比了解JVM的类加载机制JVM的类加载类似于我们执行创建表的sql脚本,最终的结果是在数据库中完成创建元数据表、表的元数据记录、业务表、业务表记录。类加载机制是与内存模型关系最紧密:就是把class文件(实际工作中主要存在jar包中)加载进内存、并且把对象实例化。

加载:把class文件读取到内存中;

准备、验证、解析:就是把class文件的数据结构在内存中映射一份,这个时候涉及到如果是静态常量就要给他初始化,如果是基本类型就要给他一个默认值,后面在理论部分详细讲解。

初始化:这个时候需要首先把类的静态变量初始化,然后把成员变量初始化,然后构造,把对象放到堆中。初始化一个类的内部成员顺序是核心,后面在理论部分详细讲解。

  • 双亲委派模型:

双亲委派有点像用复用思想做公司资源管理,底层老百姓首先跟公司请示,有没有现成的产品啊,有我就直接去卖了,如果没有我就自己做一个。

JVM的双亲委派模型:就是jvm永远认为jdk定义的类是最可信的。为什么这么说呢,首先我们要知道JVM的classloader有3个等级,BootstrapClassLoader、ExtClassLoader、AppClassLoader。

AppClassLoader是我们程序启动的基本类加载器,他负责class文件的加载。如果我们程序需要加载一个类,首先会请求AppClassLoader干活,AppClassLoader他会先问他的领导ExtClassLoader里面有没有这个类,ExtClassLoader会继续问他的领导BootstrapClassLoader里面是否有这个类,如果BootstrapClassLoader里面有就直接用了,如果没有就告诉ExtClassLoader你自己去找吧,如果ExtClassLoader里面也没有那就告诉AppClassLoader你自己去搞定吧。

 

四、JVM的垃圾回收机制

b19ce2ba8d5f9ae47df4f0e5fe6f6da7dfc.jpg

JVMHeap区域(年轻代、老年代)和方法区(永久代)结构图

(一)JVM垃圾回收机制重点

  • .JVM垃圾回收的目标:对空间内部的对象

 

  • JVM垃圾回收算法

    Mark-Sweep(标记-清除)算法

标记-清除算法分为两个阶段:标记阶段和清除阶段,标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间。有碎片问题。

    Copying(复制)算法

它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。有多花费内存空间问题。

    Mark-Compact(标记-整理)算法

算法标记阶段和Mark-Sweep一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存

    Generational Collection(分代收集)算法

分代收集算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),老年代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。

目前大部分垃圾收集器对于新生代都采取Copying算法,因为新生代中每次垃圾回收都要回收大部分对象,也就是说需要复制的操作次数较少,但是实际中并不是按照1:1的比例来划分新生代的空间的,一般来说是将新生代划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将Eden和Survivor中还存活的对象复制到另一块Survivor空间中,然后清理掉Eden和刚才使用过的Survivor空间。

而由于老年代的特点是每次回收都只回收少量对象,一般使用的是Mark-Compact算法。

 

  • JVM垃圾回收器

    Serial/Serial Old:

Serial/Serial Old收集器是最基本最古老的收集器,它是一个单线程收集器,并且在它进行垃圾收集时,必须暂停所有用户线程。Serial收集器是针对新生代的收集器,采用的是Copying算法,Serial Old收集器是针对老年代的收集器,采用的是Mark-Compact算法。它的优点是实现简单高效,但是缺点是会给用户带来停顿。

    ParNew:

ParNew收集器是Serial收集器的多线程版本,使用多个线程进行垃圾收集。

  1. Parallel Scavenge:

Parallel Scavenge收集器是一个新生代的多线程收集器(并行收集器),它在回收期间不需要暂停其他用户线程,其采用的是Copying算法,该收集器与前两个收集器有所不同,它主要是为了达到一个可控的吞吐量。    

    Parallel Old:

Parallel Old是Parallel Scavenge收集器的老年代版本(并行收集器),使用多线程和Mark-Compact算法。

    CMS:

CMS(Current Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,它是一种并发收集器,采用的是Mark-Sweep算法

    G1:

G1收集器是当今收集器技术发展最前沿的成果,它是一款面向服务端应用的收集器,它能充分利用多CPU、多核环境。因此它是一款并行与并发收集器,并且它能建立可预测的停顿时间模型。

 

(二)白话理解概念

我们尝试用数据库的管理方式与JVM垃圾回收机型进行类比理解。

上面第三节讲过JVM类加载器按照我们的要求把类加载进来,就如同执行SQL脚本进行元数据表和业务表的创建,接下来程序就开始工作,程序的这个工作过程就类似与拼命的创建业务表,创建临时表,删除临时表,向业务表中写数据记录。

这里就有一个问题,如果业务表太多了,数据库就要爆掉了,怎么办,这就有了JVM的垃圾回收机制,业务表太多了就要干掉,这里我们知道了原来垃圾回收机制是用来管理业务表的,也就是JVM的堆空间的。

JVM的垃圾回收主要是管理堆空间,因为用户程序在不停的创建对象,销毁对象,防止堆内存空间不足,就要垃圾回收。

JVM都回收哪些空间呢,目前主流的就是分代垃圾回收机制,把表空间的业务表分成两类,刚创建的表就年轻代表,长久使用的叫老年代表,年轻代的表如果用一次就不用了直接干掉,这就youngGC,老年代的表如果长时间不适用了也要干掉,这就叫oldGC。如果年轻代里面的表经常使用,就要重新归类把他分到年老代表空间中。因为JVM在垃圾回收的时候是要停止程序运行的,所以我们要尽量减少老年代的GC。

 

五、JVM架构图

1a7bdef9ba153e2a780650317162edd7250.jpg

六、JVM知识图谱

6880fc8d89eb6489435d0c5e8cfe2326f1e.jpg

 

转载于:https://my.oschina.net/u/1458864/blog/2032465

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值