结构
1.类装载子系统
2.运行时数据区(内存模型)
(1)堆,主要存储对象
(2)栈(数据栈)
线程包含栈(一个方法占一个栈帧,栈帧又包含局部变量表,操作数栈,动态链接,方法出口)、程序计数器、本地方法栈。
javap -c xx.class 可以得到反编译的可读机器码,然后根据jvm指令手册查找对应含义。这块主要涉及对局部变量表和操作数栈的操作。
(3)本地方法栈,用于执行本地方法的栈,比如new Thread.start();底层start方法有native修饰,运行到这里实际上会去找底层c语言库的实现,如dll库文件。
(4)方法区(元空间),通过类装载子系统解析把字节码文件通过一系列的解析过程放到方法区,然后字节码执行引擎把类运行起来。另外还存储常量、静态变量、类信息等
(5)程序计数器,用于记录字节码文件正在执行或将要执行的jvm代码行号,方便线程挂起后,cpu切回唤醒线程时可以继续执行。
3.字节码执行引擎,用于动态修改程序计数器的值
4.GC分类:
(1)部分收集 (Partial GC):
新⽣代收集(Minor GC / Young GC):只对新⽣代进⾏垃圾收集;
⽼年代收集(Major GC / Old GC):只对⽼年代进⾏垃圾收集。
需要注意的是 Major GC 在 有的语境中也⽤于指代整堆收集;
混合收集(Mixed GC):对整个新⽣代和部分⽼年代进⾏垃圾收集。
(2)整堆收集 (Full GC):收集整个 Java 堆和⽅法区。
5.判断对象是否死亡
(1)引用计数法
(2)可达性分析算法
6.强引⽤,软引⽤,弱引⽤,虚引⽤
(1)强引⽤(StrongReference) 以前我们使⽤的⼤部分引⽤实际上都是强引⽤,这是使⽤最普遍的引⽤。
(2)软引⽤(SoftReference) 如果⼀个对象只具有软引⽤,那就类似于可有可⽆的⽣活⽤品。如果内存空间⾜够,垃圾回收器 就不会回收它,如果内存空间不⾜了,就会回收这些对象的内存。
(3) 弱引⽤(WeakReference)如果⼀个对象只具有弱引⽤,那就类似于可有可⽆的⽣活⽤品。弱引⽤与软引⽤的区别在于:只 具有弱引⽤的对象拥有更短暂的⽣命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程 中,⼀旦发现了只具有弱引⽤的对象,不管当前内存空间⾜够与否,都会回收它的内存。
(4)虚引⽤(PhantomReference) “虚引⽤”顾名思义,就是形同虚设,与其他⼏种引⽤都不同,虚引⽤并不会决定对象的⽣命周 期。如果⼀个对象仅持有虚引⽤,那么它就和没有任何引⽤⼀样,在任何时候都可能被垃圾回 收。
虚引⽤主要⽤来跟踪对象被垃圾回收的活动。
虚引⽤与软引⽤和弱引⽤的⼀个区别在于: 虚引⽤必须和引⽤队列(ReferenceQueue)联合使 ⽤。当垃 圾回收器准备回收⼀个对象时,如果发现它还有虚引⽤,就会在回收对象的内存之前, 把这个虚引⽤加⼊到与之关联的引⽤队列中。程序可以通过判断引⽤队列中是 否已经加⼊了虚引 ⽤,来了解被引⽤的对象是否将要被垃圾回收。程序如果发现某个虚引⽤已经被加⼊到引⽤队 列,那么就可以在所引⽤的对象的内存被回收之前采取必要的⾏动。
特别注意,在程序设计中⼀般很少使⽤弱引⽤与虚引⽤,使⽤软引⽤的情况多,这是因为软引 ⽤可以加速JVM对垃圾内存的回收速度,可以维护系统的运⾏安全,防⽌内存溢出 (OutOfMemory)等问题的产⽣。
7.垃圾收集算法
(1)标记-清除算法
该算法分为“标记”和“清除”阶段:⾸先标记出所有不需要回收的对象,在标记完成后统⼀回收掉所 有没有被标记的对象。它是最基础的收集算法,后续的算法都是对其不⾜进⾏改进得到。
这种垃圾收集算法会带来两个明显的问题:
1. 效率问题 2. 空间问题(标记清除后会产⽣⼤量不连续的碎⽚)
(2)复制算法
为了解决效率问题,“复制”收集算法出现了。它可以将内存分为⼤⼩相同的两块,每次使⽤其中 的⼀块。当这⼀块的内存使⽤完后,就将还存活的对象复制到另⼀块去,然后再把使⽤的空间⼀ 次清理掉。这样就使每次的内存回收都是对内存区间的⼀半进⾏回收。
(3)标记-整理算法
根据⽼年代的特点提出的⼀种标记算法,标记过程仍然与“标记-清除”算法⼀样,但后续步骤不是 直接对可回收对象回收,⽽是让所有存活的对象向⼀端移动,然后直接清理掉端边界以外的内 存。
(4)分代收集算法
当前虚拟机的垃圾收集都采⽤分代收集算法,这种算法没有什么新的思想,只是根据对象存活周 期的不同将内存分为⼏块。⼀般将 java 堆分为新⽣代和⽼年代,这样我们就可以根据各个年代的 特点选择合适的垃圾收集算法。 ⽐如在新⽣代中,每次收集都会有⼤量对象死去,所以可以选择复制算法,只需要付出少量对象 的复制成本就可以完成每次垃圾收集。⽽⽼年代的对象存活⼏率是⽐较⾼的,⽽且没有额外的空间对它进⾏分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进⾏垃圾收集。
8.常见的垃圾收集器
(1)Serial 收集器
Serial(串⾏)收集器是最基本、历史最悠久的垃圾收集器了。⼤家看名字就知道这个收集器是 ⼀个单线程收集器了。它的 “单线程” 的意义不仅仅意味着它只会使⽤⼀条垃圾收集线程去完成 垃圾收集⼯作,更重要的是它在进⾏垃圾收集⼯作的时候必须暂停其他所有的⼯作线程( “Stop The World” ),直到它收集结束。 新⽣代采⽤复制算法,⽼年代采⽤标记-整理算法。
(2)ParNew 收集器
ParNew 收集器其实就是 Serial 收集器的多线程版本,除了使⽤多线程进⾏垃圾收集外,其余⾏ 为(控制参数、收集算法、回收策略等等)和 Serial 收集器完全⼀样。 新⽣代采⽤复制算法,⽼年代采⽤标记-整理算法。
(3)Parallel Scavenge 收集器
Parallel Scavenge 收集器也是使⽤复制算法的多线程收集器,它看上去⼏乎和 ParNew 都⼀样。Parallel Scavenge 收集器关注点是吞吐量(⾼效率的利⽤ CPU)。CMS 等垃圾收集器的关注 点更多的是⽤户线程的停顿时间(提⾼⽤户体验)。所谓吞吐量就是 CPU 中⽤于运⾏⽤户代码 的时间与 CPU 总消耗时间的⽐值。 Parallel Scavenge 收集器提供了很多参数供⽤户找到最合适 -XX:+UseParallelGC 使⽤ Parallel 收集器+ ⽼年代串⾏ -XX:+UseParallelOldGC 使⽤ Parallel 收集器+ ⽼年代并⾏ 的停顿时间或最⼤吞吐量,如果对于收集器运作不太了解,⼿⼯优化存在困难的时候,使⽤ Parallel Scavenge 收集器配合⾃适应调节策略,把内存管理优化交给虚拟机去完成也是⼀个不错 的选择。 新⽣代采⽤复制算法,⽼年代采⽤标记-整理算法。
(4)Serial Old 收集器
Serial 收集器的⽼年代版本,它同样是⼀个单线程收集器。它主要有两⼤⽤途:⼀种⽤途是在 JDK1.5 以及以前的版本中与 Parallel Scavenge 收集器搭配使⽤,另⼀种⽤途是作为 CMS 收集 器的后备⽅案。
(5)Parallel Old 收集器
Parallel Scavenge 收集器的⽼年代版本。使⽤多线程和“标记-整理”算法。在注重吞吐量以及 CPU 资源的场合,都可以优先考虑 Parallel Scavenge 收集器和 Parallel Old 收集器。
(6)CMS 收集器
CMS(Concurrent Mark Sweep)收集器是⼀种以获取最短回收停顿时间为⽬标的收集器。它 ⾮常符合在注重⽤户体验的应⽤上使⽤。 CMS(Concurrent Mark Sweep)收集器是 HotSpot 虚拟机第⼀款真正意义上的并发收集器, 它第⼀次实现了让垃圾收集线程与⽤户线程(基本上)同时⼯作。
四个步骤:初始标记、并发标记、重新标记、并发清除。
主要优点:并发收集、低停顿。但是它有下⾯三个明显的缺点: 对 CPU 资源敏感; ⽆法处理浮动垃圾; 它使⽤的回收算法-“标记-清除”算法会导致收集结束时会有⼤量空间碎⽚产⽣。
(7)G1 收集器 G1 (Garbage-First) 是⼀款⾯向服务器的垃圾收集器,主要针对配备多颗处理器及⼤容量内存的机 器. 以极⾼概率满⾜ GC 停顿时间要求的同时,还具备⾼吞吐量性能特征。
特征:
并⾏与并发:G1 能充分利⽤ CPU、多核环境下的硬件优势,使⽤多个 CPU(CPU 或者 CPU 核⼼)来缩短 Stop-The-World 停顿时间。部分其他收集器原本需要停顿 Java 线程执行的 GC 动作,G1 收集器仍然可以通过并发的⽅式让 java 程序继续执⾏。
分代收集:虽然 G1 可以不需要其他收集器配合就能独⽴管理整个 GC 堆,但是还是保留了 分代的概念。 空间整合:与 CMS 的“标记—清理”算法不同,G1 从整体来看是基于“标记整理”算法实现的 收集器;从局部上来看是基于“复制”算法实现的。
可预测的停顿:这是 G1 相对于 CMS 的另⼀个⼤优势,降低停顿时间是 G1 和 CMS 共同 的关注点,但 G1 除了追求低停顿外,还能建⽴可预测的停顿时间模型,能让使⽤者明确指 定在⼀个⻓度为 M 毫秒的时间⽚段内。
G1 收集器的运作⼤致分为以下⼏个步骤: 初始标记、并发标记、最终标记、筛选回收
G1 收集器在后台维护了⼀个优先列表,每次根据允许的收集时间,优先选择回收价值最⼤的 Region(这也就是它的名字 Garbage-First 的由来)。这种使⽤ Region 划分内存空间以及有优先 级的区域回收⽅式,保证了 G1 收集器在有限时间内可以尽可能⾼的收集效率(把内存化整为零)。
(8)ZGC 收集器
与 CMS 中的 ParNew 和 G1 类似,ZGC 也采⽤标记-复制算法,不过 ZGC 对该算法做了重⼤改进。
- 并⾏(Parallel) :指多条垃圾收集线程并⾏⼯作,但此时⽤户线程仍然处于等待状态。
并发(Concurrent):指⽤户线程与垃圾收集线程同时执⾏(但不⼀定是并⾏,可能会交 替执⾏),⽤户程序在继续运⾏,⽽垃圾收集器运⾏在另⼀个 CPU 上。