JVM介绍

1. JVM的体系架构(内存模型)

绿色的为线程公有,橘色的为线程共有

2. 类加载器

负责将.class文件加载到内存中,并且将该文件中的数据结构转换为办法区中的数据结构,生成一个Class对象

2.1 类加载器分类

  • 自启动类加载器。Bootstrap ClassLoader类加载器。负责加载jdk自带的包。

    • %JAVA_HOME%/lib/rt.jar%即JDK源码
    • 应用C++编写
    • 在程序中间接获取被该加载器加载的类的类加载器会呈现null
  • 扩大类加载器.Extension ClassLoader。负责加载jdk扩大的包

    • 便于将来扩大
    • %JAVA_HOME/lib/ext/*.jar%
  • 利用类加载器或零碎类加载器。AppClassLoader或SystemClassLOader

    • 用于加载自定义类的加载器
    • CLASSPATH门路下
  • 自定义类加载器

    • 通过实现ClassLoader抽象类实现

2.2 双亲委派机制

当利用类加载器获取到一个类加载的申请的时候,不会立刻解决这个类加载申请,而是将这个申请委派给他的父加载器加载,如果这个父加载器不可能解决这个类加载申请,便将之传递给子加载器。一级一级传递领导能够加载该类的类加载器。

该机制又称沙盒平安机制。避免开发者对JDK加载做毁坏

2.3 突破双亲委派机制

  • 自定义类加载器,重写loadClass办法
  • 应用线程上下文类加载器

2.4 Java虚拟机的入口文件

sun.misc.Launcher

3. Execution Engine

执行引擎负责执行解释命令,交给操作系统进行具体的执行

4. 本地接口

4.1 native办法

native办法指Java层面不能解决的操作,只能通过本地接口调用本地的函数库(C函数库)

4.2 Native Interface

一套调用函数库的接口

5. 本地办法栈

在加载native办法的时候,会将执行的C函数库的办法,放在这个栈区域执行

6. 程序计数器

每个线程都有程序计数器,次要作用是存储代码指令,就相似于一个执行打算。

外部保护了多个指针,这些指针指向了办法区中的办法字节码。执行引擎从程序计数器中获取下一次要执行的指令。

因为空间很小,他是以后线程执行代码的一个行号指示器/

不会引发OOM

7. 办法区

供各线程共享的运行时内存区域,寄存了各个类的构造信息(一个Class对象),包含:字段,办法,构造方法,运行时常量池。

尽管JVM标准将办法区形容为堆的一个逻辑局部,但它却还有一个别名叫做Non-Heap(非堆),目标就是要和堆离开

次要有:永恒代或者元空间。存在GC

元空间中因为间接应用物理内存的影响,所以默认的最大元空间大小为1/4物理内存大小

8. Java栈

次要负责执行各种办法,是线程公有的,随线程的沦亡而沦亡,不存在垃圾回收的问题。八大数据类型和实例援用都是在函数的栈内存中分配内存的。

默认大小为512~1024K,通过-Xss1024k参数批改

8.1 栈和队列数据结构

FILO:先进后出

队列FIFO:先进先出

8.2 存储的数据

  • 本地变量Local Variable。包含办法的形参和返回值
  • 栈操作Operand Stack。包含各种压栈和出栈操作
  • 栈帧数据Frame Data。就相当于一个个办法。在栈空间中,办法被称为栈帧

8.3 执行流程

栈中执行的单位是栈帧,栈帧就是一个个办法。

  • 首先将main办法压栈,成为一个栈帧
  • 而后调用其余办法,即再次压栈
  • 栈帧中存储了这个办法的局部变量表,操作数栈、动静链接、办法进口等
  • 栈的大小和JVM的实现无关,通常在256K~756K

9. 办法区,栈,堆的关系

10. Heap 堆

10.1 堆内存构造

默认初始大小为物理内存的1/64,默认最大大小为1/4。在理论生产中个别会将这两个值设置为雷同,防止垃圾回收器执行完垃圾回收当前还须要进行空间的扩容计算,浪费资源。

堆外内存:内存对象调配在Java虚拟机的堆以外的内存,这些内存间接受操作系统治理(而不是虚拟机),这样做的后果就是可能在肯定水平上缩小垃圾回收对应用程序造成的影响。应用未公开的Unsafe和NIO包下ByteBuffer来创立堆外内存。

默认的堆外内存大小为,通过-XX:MaxDirectMemorySize=执行堆外内存的大小

10.1.1 JDK1.7

在逻辑上划分为三个区域:

  • 新生区Young Generation Space

    • 伊甸区Eden Space
    • 幸存区Survivor 0 Space
    • 幸存区Survivor 1 Space
  • 养老区Tenure Generation Space
  • 永恒区Permanent Space(办法区)

在物理层面划分为两个区域:

  • 新生区
  • 老年区
10.1.1.1 堆内存GC过程

次要流程有三步:

  • Eden满了当前登程一次轻GC(Minor GC),没有死亡的对象,年龄+1,寄存到from区域
  • Eden再次满了当前再次触发一次GC,没有死亡的对象搁置于to区域,而后将from区域中没有死亡的对象全副置于to区域,年龄+1。之后每一次GC都会登程一次fromto的替换,哪个区域是空的那个区域就是to
  • survivor区域满了当前,再次触发GC,当存在对象的年龄等于15的时候,就会将该对象移入老年区

    • MaxTenuringThreshold通过这个参数设置当年龄为多少的时候移入
  • 老年区满了当前触发一次Full GC,如果老年区无奈再寄存对象间接报OOM

留神:每一次GC都会给存活的对象的年龄+1

10.1.2 JDK1.8

1.7相比,仅仅是将永恒代更替为了元空间。元空间的寄存内置是物理内存,而不是JVM中。

这样解决,能够使元空间的大小不再受虚拟机内存大小的影响,而是由零碎以后可用的空间来管制。

新生区和老年区的大小比例为1:2,通过-XX:NewRatio=n设置新生代和老年代的比例,n代表老年区所占的比例。

Eden Space和Survivor Space之间的比例默认为8:1,通过-XX:SurvivorRatio设置伊甸区和幸存者区的比例

逻辑层面分层:

  • 新生区Young Generation Space

    • 伊甸区Eden Space
    • 幸存区Survivor 0 Space
    • 幸存区Survivor 1 Space
  • 老年区Tenure Generation Space
  • 元空间(办法区)

物理层面分层:

  • 新生区 他占据堆的1/3
  • 老年区 他占据堆的2/3

10.2 堆参数调优

10.2.1 罕用堆参数
参数作用
-Xms设置初始堆大小,默认为物理内存的1/64
-Xmx设置最大堆大小,默认为物理内存的1/4
-XX:+PrintGCDetails输入具体的GC日志

模仿OOM

//设置最大堆内存为10m //-Xms10m -Xmx10m -XX:+PrintGCDetails

上面咱们具体分析一下GC的过程做了什么,GC日志怎么看

名称:GC以前占用->GC之后占用(总共占用)

//GC 调配失败GC (Allocation Failure)    [PSYoungGen: 1585K->504K(2560K)] 1585K->664K(9728K), 0.0009663 secs] //[新生代,以前占用->线程占用(总共闲暇)] 堆应用大小->堆当初大小(总大小)    [Times: user=0.00 sys=0.00, real=0.00 secs]         [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)]  [ParOldGen: 590K->573K(7168K)] 590K->573K(9728K), [Metaspace: 3115K->3115K(1056768K)], 0.0049775 secs]  [Times: user=0.00 sys=0.00, real=0.01 secs] 

11. 垃圾回收算法

11.1 垃圾回收类型

  • 一般GC(minor GC)产生在新生区的,很频繁
  • 全局GCmajor GC产生在老年代的垃圾收集动作,呈现一次major GC常常会随同至多一次的Minor GC

11.2 垃圾回收算法分类

11.2.1 援用计数法

次要思维:每存在一个对象援用就给这个对象加一,当这个对象的援用为零的时候,便触发垃圾回收。个别不应用

毛病:

  • 每次新创建对象就须要增加一个计数器,比拟节约
  • 循环援用较难解决
11.2.2 复制算法

次要思维:将对象间接拷贝一份,搁置到其余区域

长处:不会产生内存碎片

毛病:占用空间比拟大

应用场景:新生区的复制就是通过复制算法来执行的。当Minor Gc当前,就会幸存的对象复制一份搁置到to

11.2.3 标记革除算法

次要思维:从援用根节点遍历所有的援用,标记出所有须要清理的对象,而后进行革除。两步实现

毛病:在进行垃圾回收的时候会打断整个代码的运行。会产生内存碎片

11.2.4 标记整顿算法

次要思维:和标记革除算法一样,最初增加了一个步骤整顿,将整顿内存碎片。三步实现

毛病:效率低,须要挪动对象。

11.3 各大垃圾回收算法比拟

11.3.1 内存效率

复制算法>标记革除法>标记整顿法

11.3.2 内存参差度

复制算法=标记整顿法>标记革除法

11.3.3 内存利用率

标记整顿法=标记革除法>复制算法

11.3.4 最优算法

通过场景应用不同的算法,来达到最优的目标

年老代:因为其对象存活时间段,对象死亡率高,所以个别应用复制算法

老年代:区域大,存活率高,个别采纳标记革除和标记整顿的混合算法。

老年代个别是由标记革除或者是标记革除与标记整顿的混合实现。以hotspot中的CMS回收器为例,CMS是基于Mark-Sweep实现的,对于对像的回收效率很高,而对于碎片问题,CMS采纳基于Mark-Compact算法的Serial Old回收器做为弥补措施:当内存回收不佳(碎片导致的Concurrent Mode Failure时),将采纳Serial Old执行Full GC以达到对老年代内存的整顿。

11.3.5 GCRoots

下面咱们提到标记革除算法的时候,提到了一个名词,根节点援用。那么什么叫做根节点援用呢?

根节点援用也成GCRoots,他是指垃圾回收算法进行对象遍历的根节点。即从这个对象开始往下遍历,标记须要进行回收的对象。

垃圾回收标记的过程就是:以GCRoots对象开始向下搜寻,如果一个对象到GCRoots没有任何的援用链相连时,阐明此对象不可用。

就是从GCRoots进行遍历,能够被遍历到的就不是垃圾,没有被遍历到的就是垃圾,断定死亡

11.3.5.1 可达性对象和不可达性对象

可达性对象是指,在对象链路援用的顶层是一个GCRoot援用

不可达对象是指,在对象链路援用的顶层不是一个GCRoot援用

艰深解释:可达性对象就是对象有一个归属,这个归属有一个术语名称叫做GCRoot,不可达性对象就是这些对象没有归属。

11.3.5.2 什么援用能够作为GCRoots
  • 栈内的局部变量援用
  • 元空间中的动态属性援用
  • 元空间中的常量援用
  • 本地办法栈中native润饰的办法

说白了,就是所有裸露给开发者的援用

12. 垃圾回收器

垃圾回收器是基于GC算法实现的。

次要有四种垃圾回收器,不过具体有七种应用形式

12.1 四种垃圾回收器

12.1.1 串行垃圾回收器(Serial)

单线程进行垃圾回收,此时其余的线程全副被暂停

通过-XX:+UseSerialGC

12.1.2 并行垃圾回收器(Parallel)

多线程进行垃圾回收,此时其余的线程全副被暂停

12.1.3 并发垃圾回收器(CMS)

GC线程和用户线程同时运行

12.1.4 G1垃圾回收器

分区垃圾回收。物理上不辨别新生区和养老区,将堆内存划分为1024个小的region,每一个占据的空间在2~32M,每一个region都可能是Eden SpaceSurvivor01 SpaceSurvivor02 SpaceOld区。

整体应用了标记整顿算法,部分应用了复制算法。通过复制算法将GC后的对象从一个region向另一个region迁徙,至于造成了内存碎片问题,通过整体的标记整顿算法,防止了内存碎片的诞生

在进行垃圾回收的时候间接对一个region进行回收,保留下来的对象通过复制算法复制到TO区或者Old区。

逻辑上堆有四个区,每一个区的大小不定,按需分配。分为Eden SpaceSurvivor01 SpaceOldHumongous。其中Humongous用来寄存大对象,个别是间断存储,当因为间断region有余的时候,会触发Full GC清理四周的Region以寄存大对象

G1堆内存示意

G1垃圾回收

呈现大对象,三个region不能寄存,进行FullGC

执行流程

  • 初始标记。GC多线程,标记GCRoots
  • 并发标记。用户线程和GC线程同时进行。GC线程遍历GCRoots的所有的对象,进行标记
  • 从新标记。修改被并发标记标记的对象,因为用户程序再次调用,而须要勾销标记的对象。GC线程
  • 筛选回收。清理被标记的对象。GC线程
  • 用户线程持续运行

12.1.4.1 案例
  • 初始标记。是通过一个大对象引发的G1

  • 并发标记

  • 从新标记、筛选清理和大对象引发的Full GC

12.1.4.2 G1罕用参数
-XX:+UseG1GC  开启GC-XX:G1HeapRegionSize=n : 设置G1区域的大小。值是2的幂,范畴是1M到32M。指标是依据最小的Java堆大小划分出约2048个区域-XX:MaxGCPauseMillis=n : 最大进展工夫,这是个软指标,JVM将尽可能(但不保障)进展工夫小于这个工夫    -XX:InitiatingHeapOccupancyPercent=n  堆占用了多少的时候就触发GC,默认是45-XX:ConcGCThreads=n  并发GC应用的线程数-XX:G1ReservePercent=n 设置作为闲暇空间的预留内存百分比,以升高指标空间溢出的危险,默认值是10%

12.2 罕用参数

DefNew      Default New Generation //串行垃圾回收器,新生代叫法Tenured     Old  //串行垃圾回收器,老年代叫法ParNew         Parallel New Generation //新生代并行垃圾回收器,新生代叫法PSYongGen     Parallel Scavenge //新生代和老年代垃圾回收器,叫法ParOldGen     Parallel Old Generation //新生代和老年代垃圾回收器,叫法

12.3 新生代垃圾回收器

上图显示的是新生区和老年区能够应用垃圾回收器的所有品种,咱们一个一个来阐明

12.3.1 串行GC(Serial/Serial Coping)

新生代应用Serial Coping垃圾回收器应用复制算法

老年区默认应用Serial Old垃圾回收器,应用标记革除算法和标记整顿算法

通过-XX:+UseSerialGC设置

12.3.2 并行GC(ParNew)

新生区应用ParNew垃圾回收器,应用复制算法

老年区应用Serial Old垃圾回收器(不举荐这样应用),应用标记革除算法和标记整顿算法

通过-XX:+UseParNewGC启动

12.3.3 并行回收GC(Parallel/Parallel Scavenge)

新生代应用并行垃圾回收

老年代应用并行垃圾回收。Java1.8中默认应用的垃圾回收器

一个问题:Parallel和Parallel Scavenge收集器的区别?

Parallel Scavenge收集器相似于ParNew也是一个新生代的垃圾收集器,应用了复制算法,也是一个并行的多线程的垃圾收集器,俗称吞吐量优先收集器。

parallel Scavenge是一种自适应的收集器,虚构机会依据以后零碎运行状况收集性能监控信息,动静调整这些参数以提供最合适的提顿工夫或者最大吞吐量

他关注的点是:

可管制的吞吐量。吞吐量=运行用户代码工夫/(运行用户代码工夫+垃圾收集工夫),

同时,当新生代抉择为Parallel Scavenge的时候,会默认激活老年区应用并行垃圾回收

通过-XX:UseParallelGC或者-XX:UseParallelOldGC两者会相互激活

-XX:ParallelGCThreads=n示意启动多少个GC线程

cpu>8时 N=5或者8

cpu<8时 N=理论个数

12.4 老年代垃圾回收器

12.4.1 串行垃圾回收器(Serial Old/Serial MSC)

Serial OldSerial垃圾收集器老年代版本,是一个单线程的收集器,应用标记整顿算法,运行在Client中的年轻代垃圾回收算法

与新生代的Serial GC相关联

12.4.2 并行回收(Parallel Old/Parallel MSC)

Parallel Old/采纳标记整顿算法实现

与新生代的Parallel Scavenge GC相关联

12.4.3 并发标记革除GC

CMS收集器(Concurrent Mark Sweep并发标记革除):一种以获取最短回收进展工夫为指标的收集器

适宜利用在互联网站或者B/S零碎的服务器上,器重服务器的响应速度

CMS非常适合堆内存大、CPU核数多的服务端利用,也是G1呈现之前大型利用的首选收集器

标记的时候,GC线程运行;革除的时候和用户线程一起运行

通过-XX:+UseConcMarkSweepGC指令开启

配合新生区的pallellal New GC回收器应用

当CMS因为CPU压力太大无奈应用的时候会应用SerialGC作为备用收集器

12.4.3.1 CMS执行过程
  • 初始标记(CMS initial mark)。遍历寻找到所有的GCRootsGC线程执行,用户线程暂停
  • 并发标记(CMS concurrent mark)和用户线程一起遍历GCRoots,标记须要革除的对象
  • 从新标记(CMS remark)。修改标记期间,对因用户程序持续运行而不须要进行回收的对象进行修改
  • 并发革除(CMS concurrent sweep)和用户线程一起革除所有标记的对象

12.4.3.2 优缺点

长处:

  • 并发收集低进展

毛病:

  • 并发执行,对CPU资源压力大
  • 采纳标记革除算法会导致大量的内存碎片

12.5 垃圾回收器小结

参数(-XX:+……)新生代垃圾回收器新生代算法老年代垃圾回收器老年代算法
UseSerialGCSerialGC复制算法Serial Old GC标整
UseParNewGCParallel New GC复制算法Serial Old GC标整
UseParllelGCParallel Scavenge GC复制算法Parallel GC标整
UseConcMarkSweepGCParallel New GC复制算法CMS和Serial Old GC标清
UseG1GC整体标整部分复制

垃圾回收算法通用逻辑

12.6 CMS和G1的区别

  • G1不会引发内存碎片
  • G1对内存的精准管制,能够精准的去收集垃圾。依据设置的GC解决工夫去收集垃圾最多的区域

13. JMM

java内存模型。是一种标准。

线程在操作变量的时候,首先从物理内存中复制一份到本人的工作内存中(栈内存),更新当前再写入物理内存中

特点:

  • 原子性
  • 可见性
  • 有序性

更多原创文章和学习教程请关注笔者同名公众号@MakerStack获取