JDK7以及之前的版本,堆空间包括以下部分:
JDK 8 版本之后 PermGen(永久) 已被 Metaspace(元空间) 取代,元空间使用的是直接内存 。
1、对象优先在 Eden 区分配
大多数情况下,对象在新生代中 Eden 区分配。当 Eden 区没有足够空间进行分配时,虚拟机将发起一次 Minor GC。
2、大对象直接进入老年代
大对象就是需要大量连续内存空间的对象(比如:字符串、数组),大对象直接进入老年代是一种优化策略,旨在避免将大对象放入新生代,从而减少新生代的垃圾回收频率和成本。
3、长期存活的对象将进入老年代
对象都会首先在 Eden 区域分配。如果对象在 Eden 出生并经过第一次 Minor GC 后仍然能够存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间(s0 或者 s1)中,并将对象年龄设为 1(Eden 区->Survivor 区后对象的初始年龄变为 1)。
对象在 Survivor 中每熬过一次 MinorGC,年龄就增加 1 岁,当它的年龄增加到一定程度(默认为 15 岁),就会被晋升到老年代中。
针对 HotSpot VM 的实现,它里面的 GC 其实准确分类只有两大种:
部分收集 (Partial GC):
整堆收集 (Full GC):收集整个 Java 堆和方法区
空间分配担保是为了确保在 Minor GC 之前老年代本身还有容纳新生代所有对象的剩余空间。只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小,就会进行 Minor GC,否则将进行 Full GC。
一、引用计数法
给对象中添加一个引用计数器:
这个方法实现简单,效率高,但是目前主流的虚拟机中并没有选择这个算法来管理内存,其最主要的原因是它很难解决对象之间循环引用的问题。
二、可达性分析算法
这个算法的基本思想就是通过一系列的称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的,需要被回收。
哪些对象可以作为 GC Roots 呢?
对象可以被回收,就代表一定会被回收吗?
即使在可达性分析法中不可达的对象,也并非是一定被回收的,要真正宣告一个对象死亡,至少要经历两次标记过程;可达性分析法中不可达的对象被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行 finalize
方法。当对象没有覆盖 finalize
方法,或 finalize
方法已经被虚拟机调用过时,虚拟机将这两种情况视为没有必要执行。
被判定为需要执行的对象将会被放在一个队列中进行第二次标记,除非这个对象与引用链上的任何一个对象建立关联,否则就会被真的回收。
1、强引用(Strong Reference):强引用是最常见的引用类型。如果一个对象具有强引用,即使系统中内存不足,垃圾回收器也不会回收该对象。只有在没有任何强引用指向该对象时,该对象才会被垃圾回收。
2、软引用(Soft Reference):软引用用于描述一些还有用但非必需的对象。在系统将要发生内存溢出之前,会尝试回收软引用指向的对象。这样可以允许程序保持对那些可能使用的对象进行缓存,同时避免了内存溢出。
3、弱引用(Weak Reference):弱引用比软引用更弱一些。当垃圾回收器工作时,无论内存是否充足,都会回收被弱引用指向的对象。弱引用通常用于实现对象缓存,当对象被弱引用指向时,可以更容易地被垃圾回收器回收。
4、虚引用(Phantom Reference):虚引用是最弱的一种引用关系。虚引用的存在不会对对象的生存时间造成影响,也不能通过虚引用来获取被引用的对象。它主要用于跟踪对象被垃圾回收的状态,当一个对象被虚引用指向时,表示该对象已经进入了finalization阶段,当垃圾回收器准备回收该对象时,会将这个虚引用插入一个引用队列中。
**虚引用必须和引用队列(ReferenceQueue)联合使用,弱引用不是必须与引用队列联合使用。**当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
特别注意,在程序设计中一般很少使用弱引用与虚引用,使用软引用的情况较多,这是因为软引用可以加速 JVM 对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生。
运行时常量池主要回收的是废弃的常量。那么,我们如何判断一个常量是废弃常量呢?
JDK1.7 之前运行时常量池逻辑包含字符串常量池存放在方法区, 此时 hotspot 虚拟机对方法区的实现为永久代
JDK1.7 字符串常量池被从方法区拿到了堆中, 这里没有提到运行时常量池,也就是说字符串常量池被单独拿到堆,运行时常量池剩下的东西还在方法区, 也就是 hotspot 中的永久代 。
JDK1.8 hotspot 移除了永久代用元空间(Metaspace)取而代之, 这时候字符串常量池还在堆, 运行时常量池还在方法区, 只不过方法区的实现从永久代变成了元空间(Metaspace)
假如在字符串常量池中存在字符串 “abc”,如果当前没有任何 String 对象引用该字符串常量的话,就说明常量 “abc” 就是废弃常量,如果这时发生内存回收的话而且有必要的话,“abc” 就会被系统清理出常量池了。
类需要同时满足下面 3 个条件才能算是 “无用的类”:
ClassLoader
已经被回收。java.lang.Class
对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。虚拟机可以对满足上述 3 个条件的无用类进行回收,这里说的仅仅是“可以”,而并不是和对象一样不使用了就会必然被回收。
1、标记-清除算法
标记-清除(Mark-and-Sweep)算法分为“标记(Mark)”和“清除(Sweep)”阶段:首先标记出所有不需要回收的对象,在标记完成后统一回收掉所有没有被标记的对象。
缺点:
2、复制算法
它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。
优点:解决了标记-清除算法的内存碎片和效率不高的问题。
缺点:
3、标记-整理算法
在标记阶段找出存活对象后,将它们向一端移动,然后直接清理掉边界以外的内存,从而解决了内存碎片问题。
由于多了整理这一步,因此效率也不高,适合老年代这种垃圾回收频率不是很高的场景。
4、分代收集算法
将堆内存分为新生代和老年代,分别采用不同的垃圾回收算法。新生代通常采用“标记-复制”算法,老年代则采用“标记-清除”或“标记-整理”算法,以更好地适应对象的生命周期特点。
优点:能够根据对象生命周期不同的特点采用不同的算法,提高垃圾回收效率。
HotSpot(即Oracle JDK和OpenJDK中的Java虚拟机实现)之所以将堆内存分为新生代和老年代,是为了更好地适应对象的生命周期特点,并采用分代收集的垃圾回收算法来提高内存利用率和垃圾回收效率。
新生代中的对象通常具有较短的生命周期,而且经过一次或多次垃圾回收后就会被回收掉,因此新生代采用了“复制”算法(即标记-复制算法)。通过将存活的对象复制到另一个区域,并清除掉原来区域的内容来进行内存回收,避免了对整个堆空间的遍历标记和内存碎片化问题。
而老年代中的对象则具有较长的生命周期,有些对象甚至存活于整个应用程序的生命周期中,所以采用“复制”算法并不切实际,因此老年代通常采用“标记-清除”或“标记-整理”算法来进行垃圾回收,以便充分利用内存空间并避免内存碎片问题。
通过将堆内存分为新生代和老年代,并针对它们的特点采用不同的垃圾回收算法,HotSpot能够更有效地管理内存、提高垃圾回收效率,并在一定程度上减少应用程序的停顿时间,从而提升整体性能和用户体验。
1、Serial 收集器
它是一个单线程的收集器,不仅仅意味着它只会使用一条垃圾收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集工作的时候必须暂停其他所有的工作线程( “Stop The World” ),直到它收集结束,适用于单核处理器或小型内存环境。
2、ParNew 收集器
ParNew收集器其实就是 Serial 收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和 Serial 收集器完全一样。
3、Parallel Scavenge收集器
Parallel Scavenge 收集器 也称为吞吐量优先垃圾回收器,它使用多个线程并行进行垃圾回收,提供了很多参数供用户找到最合适的停顿时间或最大吞吐量,适用于多核处理器和需要提高吞吐量的场景。
4、Parallel Scavenge 收集器
Parallel Scavenge 收集器的老年代版本。使用多线程和“标记-整理”算法。
5、CMS(Concurrent Mark Sweep)收集器
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它非常符合在注重用户体验的应用上使用。是 HotSpot 虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。
CMS 收集器是一种 “标记-清除”算法实现的
整个过程分为四个步骤:
主要优点:并发收集、低停顿
缺点:
6、G1收集器
G1 (Garbage-First) 是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器。以极高概率满足 GC 停顿时间要求的同时,还具备高吞吐量性能特征。
G1 收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的 Region(这也就是它的名字 Garbage-First 的由来) 。这种使用 Region 划分内存空间以及有优先级的区域回收方式,保证了 G1 收集器在有限时间内可以尽可能高的收集效率(把内存化整为零)
它具备以下特点:
5、CMS(Concurrent Mark Sweep)收集器
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它非常符合在注重用户体验的应用上使用。是 HotSpot 虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。
CMS 收集器是一种 “标记-清除”算法实现的
整个过程分为四个步骤:
主要优点:并发收集、低停顿
缺点:
6、G1收集器
G1 (Garbage-First) 是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器。以极高概率满足 GC 停顿时间要求的同时,还具备高吞吐量性能特征。
G1 收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的 Region(这也就是它的名字 Garbage-First 的由来) 。这种使用 Region 划分内存空间以及有优先级的区域回收方式,保证了 G1 收集器在有限时间内可以尽可能高的收集效率(把内存化整为零)
它具备以下特点:
Minor GC主要负责回收年轻代的垃圾对象,执行速度较快,停顿时间短;而Full GC则是全局性的垃圾回收,涉及整个堆内存的回收,执行速度较慢,停顿时间较长