文档章节

白话理解JVM工作原理

飓风2000
 飓风2000
发布于 2018/09/09 21:45
字数 3316
阅读 15
收藏 0

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

白话理解JVM的基本概念

一、JVM知识点的3条主线

    1.JVM的内存模型

    2.JVM的类加载机制

    3.JVM的垃圾回收机制

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

 

二、JVM内存模型图

基本内存模型

核心内存模型

 

(一)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的类加载机制

类加载过程图

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

(一)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的垃圾回收机制

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架构图

六、JVM知识图谱

 

© 著作权归作者所有

共有 人打赏支持
飓风2000
粉丝 38
博文 337
码字总数 138498
作品 0
浦东
高级程序员
私信 提问
Java 线程本地 ThreadLocal 的分析和总结

ThreadLocal类在Spring,Hibernate等框架中起到了很大的作用,对于其工作原理,很多网上的文章分析的不够彻底,甚至有些误解。 首先,为了解释ThreadLocal类的工作原理,必须同时介绍与其工作...

绿悠悠
2010/10/07
2.1K
0
搜狐(北京) 高薪招聘 Java 项目经理/架构师/WEB开发工程师

北京搜狐 高薪招聘 Java 项目经理/架构师/WEB开发工程师 有意请联系我 或帮忙转发 联系方式 cnscud@gmail.com msn:cloud@cnscud.com 多谢支持 注明来自 oschina.net更好, 谢谢 薪水范围 10-2...

飞云小侠
2011/05/08
816
7
java两年开发经验,北京,本科,有java培训经历,求推荐工作

1,大学信息与计算科学专业,并在java培训机构培训过,深刻理解面向对象 2,熟练使用js,extjs,jquery,ajax,div+css等前端技术 3,熟练使用ssh框架,并亲自实现过其中的ioc,aop,or—map...

小虫大人
2014/03/04
621
7
[北京] 搜狐招聘Java技术Leader, 架构师, 资深工程师

搜狐公司, www.sohu.com 具体邮件联系 felixzhang@sohu-inc.com 咨询 msn: cloud@cnscud.com 常规薪水范围 10K-30K 牛人上不封顶 --------------------------------------- · Java项目经理 ......

飞云小侠
2012/02/02
1K
7
[北京]搜狐高薪招聘JS前端/页面制作/Java架构/开发/运维/搜索

北京搜狐为了构建一个重要的互联网产品, 打造一支精英化队伍, 特招聘有理想有抱负 的同学, 来实现我们共同的理想! 你是否胸中理想? 你是否需要一个大的舞台? 你是否需要一帮聊得来的兄弟, 请...

飞云小侠
2011/07/11
1K
11

没有更多内容

加载失败,请刷新页面

加载更多

Redis之父表示ARM服务器没戏!

ARM表示Neoverse N1平台和E1 CPU即将发布,Neoverse N1和E1采用7nm制程,并且为服务器和通信设备增加重要提升,拥有高可扩展性、高处理量以及高性能,将分别在2020年和2021年投入使用。 与C...

linuxCool
16分钟前
2
0
YARN 内存参数终极详解

YARN环境中应用程序JAR包冲突问题的分析及解决 Hadoop框架自身集成了很多第三方的JAR包库。Hadoop框架自身启动或者在运行用户的MapReduce等应用程序时,会优先查找Hadoop预置的JAR包。这样的...

stys35
21分钟前
0
0
Mybatis常用总结一

这一节的学习强烈建议实际操作 主键自增和自定义主键 实现描述:我们在写程序时,时常会遇到这样的问题:插入一条数据,但因有主键的存在,而有时候会报“违反唯一性约束”的错误,我们可以使...

大笨象会跳舞吧
21分钟前
0
0
计数二进制子串

leetcode:https://leetcode.com/problems/count-binary-substrings/ 给定一个字符串 s,计算具有相同数量0和1的非空(连续)子字符串的数量,并且这些子字符串中的所有0和所有1都是组合在一起...

woshixin
24分钟前
0
0
mysql utf8mb4字符集,存储emoji表情

字符集utf8mb4 utf8mb4兼容utf8,且比utf8能表示更多的字符。 看unicode编码区 从1 ~ 126就属于传统utf8区,当然utf8mb4也兼容这个区,126行以下就是utf8mb4扩充区,什么时候你需要存储那些...

我心中有猛狗
27分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部