1. JVM是什么

JVM全称Java Virtual Machine,Java虚拟机。用来解析运行字节码程序,JVM消除平台差异性实现跨平台。
JVM.png

2 JVM运行时数据区

2.1 线程私有

2.1.1 栈(Stack)

Java虚拟机 - 图2

  • 栈是运行时的单位。
  • 每个方法在执行的同时都会创建一个栈帧,用来存放局部变量,对象的引用之类的方法信息。
  • 栈不存在垃圾回收,但是存在OOM和StackOverflowError。

    2.1.2 本地方法栈(Native Method Stack)

    虚拟机用到的 Native 方法,与底层相关。

    2.1.3 程序计数器(PC Register)

    是每个线程都具有一个私有的程序计数器,因为JVM执行代码是一行一行的执行,所以需要计数器来记录当前执行的行数。

2.2 线程公有

2.2.1 堆(Heap)

  • 堆区用来存储对象实例及数组值,可以认为java中所有通过new创建的对象都在此分配。
  • 堆是GC执行垃圾回收的重点,在方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才会被移除。
  • 堆分为新生代和老年代。新生代的特点是每次垃圾回收时都有大量的对象需要被回收。老年代的特点是每次垃圾收集时只有少量对象需要被回收。
  • 对象的分配过程

    • new的对象先放伊甸园区,若是大对象则直接放老年区
    • 当伊甸园的空间已满的时候创建对象,会触发Minor GC,将伊甸园区中的没有被引用的对象进行销毁。存活对象移动到Survivor(From),对象年龄加一
    • 再次触发Minor GC时,将Eden和Survivor(From)中还存活的对象复制到另一块Survivor(To)中,然后清理掉Eden和刚才使用过的Survivor(From),然后两个Survivor互换,对象年龄加一
    • 当对象年龄大于15则移动至老年区
    • 当老年区内存不足时,会触发Old GC,若执行了Old GC之后发现依然无法进行对象的保存,就会产生OOM异常

      2.2.2 方法区

  • 方法区是线程共享的区域,它一般用于储存与类相关的信息,像类信息、常量、静态变量,即编译器编译后的代码

    1. jdk1.7中它的实现是永久代,缺点是经常会出现内存溢出。在jdk8中,实现为元空间,元空间直接使用本地内存,理论上电脑有多少内存,它就有多少内存,避免的内存溢出问题
  • 运行时的常量池存放在元空间里,除了字符串常量池,字符串常量池存放在堆里

3 类加载机制

3.1 类加载是什么

类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区
在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口.

3.2 类加载器及双亲委派原则

双亲委派是当一个类加载器收到类加载任务,会先交给其父类加载器去完成,因此最终加载任务都会传递到顶层的启动类加载器,只有当父类加载器无法完成加载任务时,才会尝试执行加载任务。

  • 可以避免重复加载,保证java中类名的唯一性
  • 保证java核心api中定义类型不会被随意替换,避免了核心API类不被修改

    3.3 类的生命周期

    加载:根据查找路径找到响应的.class文件加载进内存,并为之创建Class对象
    验证:保证被加载类的正确性
    准备:为类的静态变量分配内存,并初始化为默认值。这一步和初始化有区别。比如static int a=1。那么到这部分配默认值是0,初始化的时候才是1.
    解析:虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用就理解为一个标示,而在直接引用直接指向内存中的地址;
    初始化:对静态变量和静态代码块执行初始化工作。
    使用
    卸载(垃圾回收)

4 垃圾回收机制

4.1 标记垃圾对象

Java虚拟机 - 图3

4.2 GC分类

Java虚拟机 - 图4

4.3 垃圾回收算法

4.3.1 标记-清除算法

采用从根集合进行扫描,对存活的对象进行标记,标记完毕后,再扫描整个空间中未被标记的对象进行直接回收,不整理剩余对象会导致内存碎片。
Java虚拟机 - 图5

4.3.2 复制算法

将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。
Java虚拟机 - 图6
4.3.3 标记-整理算法
标记整理是标记-清除算法的升级,标记清除分为标记和清除两个阶段。标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间。但这样容易产生内存碎片,而升级的标记-整理不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存,充分利用内存空间。
Java虚拟机 - 图7

4.3.4 分代收集算法

目前大部分JVM的垃圾收集器采用的算法。
新生代内存的回收(Minor GC)主要采用复制算法
而对于老年代的回收(Major GC),大多采用标记-整理算法

4.3 垃圾收集器

image.png

收集器 串行、并行or并发 生代 算法 目标 适用场景
Serial 串行 新生代 复制算法 响应速度优先 单CPU环境下的Client模式
Serial Old 串行 老年代 标记-整理 响应速度优先 单CPU环境下的Client模式、CMS的后备预案
ParNew 并行 新生代 复制算法 响应速度优先 多CPU环境时在Server模式下与CMS配合
Parallel Scavenge 并行 新生代 复制算法 吞吐量优先 在后台运算而不需要太多交互的任务
Parallel Old 并行 老年代 标记-整理 吞吐量优先 在后台运算而不需要太多交互的任务
CMS 并发 老年代 标记-清除 响应速度优先 集中在互联网站或B/S系统服务端上的Java应用
G1 并发 both 标记-整理+复制算法 响应速度优先 面向服务端应用,将来替换CMS

5 JVM调优

5.1 参数类型

Java虚拟机 - 图9

5.2 JVM自带调优工具

jps 主要用来输出JVM中运行的进程状态信息
jstat 显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据
jstack 查看某个Java进程内的线程堆栈信息,根据堆栈信息我们可以定位到具体代码
jmap 进程内存使用情况dump到文件中,再用jhat分析查看