理解JVM虚拟机的工作原理

理解JVM虚拟机的工作原理

什么是JVM?

是运行所有Java程序的抽象计算机,运行所有Java程序的抽象计算机,是Java语言的运行环境,它是Java 最具吸引力的特性之一。

虚拟机jvm就是一个操作系统中的进程实例。

JVM的内存模型

所有对象都在这里分配内存,是垃圾回收的主要区域。

方法区

用于存放加载的类信息、常量、静态变量、即时编译后的代码等数据。

运行时常量池

Class文件的常量池,会在类加载后被放入这个区域

jJava虚拟机栈

用于存放局部变量、操作数栈、常量池引用等信息。

本地方法区

本地⽅法⼀般是⽤其它语⾔(C、C++ 或汇编语⾔等)编写的,并且被编译为基于本机硬件和操作系统 的程序,对待这些⽅法需要特别处理。

程序计数器

记录正在执⾏的虚拟机字节码指令的地址

java虚拟机栈、本地方法区、程序计数器为线程私有。

垃圾收集

垃圾收集主要是在堆和方法区进行

方法区的回收主要是对常量池的回收和类的卸载。

当一个对象被回收时,如果需要执行finalize()方法,那么对象会被重新引用,从而实现复活,再第二次回收的时候就不会再被复活。

如何判断是否为垃圾?
  1. 可达性分析算法(根搜索算法)

以 GC Roots 为起始点进⾏搜索,可达的对象都是存活的,不可达的对象可被回收。

GC Roots分为:

  • 虚拟机中虚拟机栈的引用变量
  • 方法区静态属性引用变量
  • 方法区常量属性引用变量
  • 本地方法栈JNI引用变量
  1. 引用计数算法

对象被引用时,引用计数器加1,当对象引用失效后,引用计数器减1。当引用计数器的值为0的时候,对象就可被回收。

缺点:互相引用的对象不会被回收。

引用类型有哪几种?

强引用

使用new的方式来创建强引用,强引用的对象不会被回收。

软引用 (SoftReference)

通过SoftReference类来创建,只用当内存不足的时候才会被回收。

弱引用 (WeakReference)

通过WeakReference、来创建,只能存活到下一次垃圾回收发生之前。

虚引用 (PhantomReference)

使用PhantomReference类来创建,一个对象是否存在虚引用对其生存时间不影响。

常见的垃圾收集算法
标记-清除算法

描述: 首先标记需要回收的对象,在标记完成后统一回收所有被标记的对象,标记方法使用 根搜索算法。

缺点:1、标记和清除的效率都不高 2、会产生大量内存碎片,导致大对象无法分配内存。

标记-整理算法

描述:标记方法同上,但后续将所有存活的对象移动到内存的一端,然后清除掉端外的对象。

缺点:1、需要移动大量对象,处理效率比较低。

优点:1、不会产生内存碎片。

复制回收算法

描述:将内存分为一块Eden空间和两块较小的Survivor空间,每次使用eden空间和一块survivor空间,在回收时将eden和其survivor空间存活的对象复制到另一块survivor空间,最后清除eden和其survivor空间。

缺点: 将内存一分为二,导致内存使用率大大降低

优点:有效避免内存碎片

分代回收算法

描述:内存分为新生代和老年代,在新生代采用复制算法,在老年代采用标记-清除和标记-整理算法。

垃圾收集器

Serial 收集器 : 它是单线程的收集器,只会使⽤⼀个线程进⾏垃圾收集⼯作。

ParNew 收集器:它是 Serial 收集器的多线程版本。

Parallel Scavenge 收集器:其它收集器⽬标是尽可能缩短垃圾收集时⽤户线程的停顿时间,⽽它的⽬标是达到⼀个可控制的吞吐 量,因此它被称为“吞吐量优先”收集器。这⾥的吞吐量指 CPU ⽤于运⾏⽤户程序的时间占总时间的⽐ 值。

Serial Old 收集器:是 Serial 收集器的⽼年代版本,

Parallel Old 收集器:是 Parallel Scavenge 收集器的⽼年代版本。

CMS收集器: CMS(Concurrent Mark Sweep),Mark Sweep 指的是标记 - 清除算法。通过增量标记来解决漏标。

G1收集器:对于堆内存不分老年代和新生代。划分为一个一个小内存块,叫做region。

STW: Stop-The-World 垃圾回收算法执行过程中,需要将jvm内存冻结的一种状态。

三色标记: 是一种逻辑上的抽象,将每个内存对象标记分为3个颜色: 黑色表示自己和成员变量都已经标记完毕。 灰色:自己标记完毕了,但成员变量还没有完全标记。白色:自己未标记

内存回收
Minor GC

Minor GC:回收新⽣代,因为新⽣代对象存活时间很短,因此 Minor GC 会频繁执⾏,执⾏的速度 ⼀般也会⽐较快

触发条件: 对于 Minor GC,其触发条件⾮常简单,当 Eden 空间满时,就将触发⼀次 Minor GC。

Full GC

回收⽼年代和新⽣代,⽼年代对象其存活时间⻓,因此 Full GC 很少执⾏,执⾏速度会⽐ Minor GC 慢很多

触发条件:

  • 调⽤ System.gc()

只是建议虚拟机执⾏ Full GC,但是虚拟机不⼀定真正去执⾏。不建议使⽤这种⽅式,⽽是让虚拟机管 理内存。

  • ⽼年代空间不⾜

⽼年代空间不⾜的常⻅场景为前⽂所讲的⼤对象直接进⼊⽼年代、⻓期存活的对象进⼊⽼年代等。

  • 空间分配担保失败

使⽤复制算法的 Minor GC 需要⽼年代的内存空间作担保,如果担保失败会执⾏⼀次 Full GC。

  • JDK 1.7 及以前的永久代空间不⾜

在 JDK 1.7 及以前,HotSpot 虚拟机中的⽅法区是⽤永久代实现的,永久代中存放的为⼀些 Class 的信 息、常量、静态变量等数据。

  • Concurrent Mode Failure

执⾏ CMS GC 的过程中同时有对象要放⼊⽼年代,⽽此时⽼年代空间不⾜(可能是 GC 过程中浮动垃 圾过多导致暂时性的空间不⾜),便会报 Concurrent Mode Failure 错误,并触发 Full GC。

内存分配策略

对象优先在 Eden 分配

⼤多数情况下,对象在新⽣代 Eden 上分配,当 Eden 空间不够时,发起 Minor GC。

⼤对象直接进⼊⽼年代

⼤对象是指需要连续内存空间的对象,最典型的⼤对象是那种很⻓的字符串以及数组。(-XX:PretenureSizeThreshold,⼤于此值的对象直接在⽼年代分配)

⻓期存活的对象进⼊⽼年代

为对象定义年龄计数器,对象在 Eden 出⽣并经过 Minor GC 依然存活,将移动到 Survivor 中,年龄就 增加 1 岁,增加到⼀定年龄则移动到⽼年代中。(-XX:MaxTenuringThreshold ⽤来定义年龄的阈值。)

动态对象年龄判定

如果在 Survivor 中相同年龄所有对象⼤⼩的总和⼤于 Survivor 空间的⼀半,则年龄⼤于或等于该年龄的对象可以直接进 ⼊⽼年代,⽆需等到 MaxTenuringThreshold 中要求的年龄。

空间分配担保

类加载机制

类是在运⾏期间第⼀次使⽤时动态加载的,⽽不是⼀次性加载所有类。因为如果⼀次性加载,那么会占 ⽤很多的内存。

每个类加载器都有缓存,查询类加载器时先依次去AppClassLoader缓存–>ExtClassLoader缓存–>BootsrapClassLoader缓存

双亲委派:向上委托查找,向下委托加载。 作用:包含Java的层的类不被应用程序覆盖。

类的⽣命周期

  1. 加载(Loading)
  2. 验证(Verification)
  3. 准备(Preparation)
  4. 解析(Resolution)
  5. 初始化(Initialization)
  6. 使⽤(Using)
  7. 卸载(Unloading)

类加载器分类

  • 启动类加载器(Bootstrap ClassLoader),使⽤ C++ 实现,是虚拟机⾃身的⼀部分

  • 所有其它类的加载器,使⽤ Java 实现,独⽴于虚拟机,继承⾃抽象类 java.lang.ClassLoader。

    扩展类加载器(Extension ClassLoader)、应⽤程序类加载器(Application ClassLoader)

你可能感兴趣的:(Java,jvm)