JVM垃圾回收详解

JVM垃圾回收详解

Java的自动内存管理主要指的是针对对象的回收和分配。堆是垃圾收集器管理的主要区域,也被称为GC堆。

现阶段,收集器基本都采用分代垃圾收集算法,所以Java堆被划分为了几个不同的区域,可以根据这些区域选择适合的垃圾收集算法。

内存分配和回收原则

大多数情况下,对象在Eden区分配,当Eden区中没有足够的空间分配时,虚拟机将发起Minor GC(新生代GC)。找出Eden区和S0区活跃的对象,将它复制到S1区。并将S0区域和Eden区的对象清空。之后将S0区和S1区交换。(Eden—> Survivor区后对象的初始年龄变为1)

如果Servivor空间满了,会通过分配担保机制,把新生代的对象提前转移到老年代去。

  • 为什么有两个Survibor(幸存者)区因为假设设想一下只有一个 Survibor 区 那么就无法实现对于 S0 区的垃圾收集,以及分代年龄的提升。

在存储大量连续内存空间的对象时,直接进入老年区。

  • 主要为了避免大对象在分配内存时由于分配担保机制带来的复制而降低效率。
长期存活的对象进入老年代

大部分情况,对象首先在Eden区域分配,如果对象在Eden出生并经过第一次Minor GC后依然存活,并且能够被Survivor容纳,将被移动到Survivor空间,对象年龄 + 1.

对象在Survivor中每熬过一次Minor GC,年龄就增加1岁。当年龄增加到一定程度,就会晋升到老年代中。

主要进行gc的区域

针对HosSpot VM 的实现,它里面的GC准确分类只有两大类:

部分收集(Partial GC):

  • 新生代收集(Minor GC / Young GC):只对新生代进行垃圾收集。

  • 老年代收集(Major GC / Old GC): 只对老年代进行垃圾收集。

  • 混合收集(Mixed GC): 对整个咸亨带和部分老年代进行垃圾收集。

整堆收集(Full GC):收集整个Java堆和方法区。

空间分配担保

对象进入老年代的情况:

  1. 大量对象在Minor GC 之后存活,但是Survivor空间不足以容纳对象。
  2. 年龄达到进入老年代的要求。
  3. 大对象直接进入老年代。

这时老年代就会进行空间分配担保机制。

JDK 6 Update 24 之后:在发生Minor GC 之前,虚拟机必须先检查老年代最大可用的连续空间大于新生代对象总大小或者历次晋升的平均大小,就会进行Minor GC ,否则进行Full GC。来确保Minor GC 之前,老年代本身还有容纳新生代所有对象的剩余空间。

死亡对象判断方法

对堆内存回收前的第一步就是要判断哪些对象已经死亡。

引用计数法

给对象添加一个引用计数器,有地方引用就加1,计数器为0,就说明不再被使用。

这个方法实现简单,效率高,但是目前主流的虚拟机中并没有选择这个算法来管理内存,其最主要的原因是它很难解决对象之间相互循环引用的问题。

假如两个对象相互循环引用,无法通过引用计数算法通知GC 回收器回收。

可达性分析算法

垃圾回收器通过GC Roots 的对象作为起点,从这些节点开始向下遍历对象引用链,如果一个对象没有任何引用与之相连,判断对象已经死亡。GC Roots 包括以下几种:

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JNI(即一般说的Native方法)引用的对象
引用类型总结

引用类型是Java垃圾回收机制的重要组成部分,它们可以帮助开发者更好地控制对象的生命周期,有效避免内存泄漏和OutOfMemoryError等问题。

JDK1.2 以后,Java 对引用的概念进行了扩充,将引用分为强引用、软引用、弱引用、虚引用四种(引用强度逐渐减弱)

  1. 强引用(StrongReference):最常见的引用类型。当我们使用 new 创建一个对象时,默认是强引用。只要强引用存在,垃圾回收器就不会回收对象。
  2. 软引用(Soft Referenc):当内存空间不足时,垃圾回收器可能会回收软引用指向的对象。软引用通常用于实现缓存,当缓存的对象在内存空间不足时被回收,可以避免OutOfMemoryError异常
  3. 弱引用(Weak Reference): 弱引用指向的对象只能生存到下一次垃圾回收之前。当垃圾回收器扫描到一个只有弱引用与之关联的对象时,该对象就会被回收。
  4. 虚引用(Phantom Reference):虚引用和弱引用类似,但是虚引用get()方法始终返回null,无法返回引用的对象。虚引用主要用于跟踪对象被垃圾回收的状态。
如何判断一个常量是废弃常量?

在Java中,字符串常量池中的字符串常量是共享的,一旦创建,就会一直存在于字符串常量池中,直到JVM退出。如果一个字符串常量没有任何对象引用它,也就是说它没有被使用,那么这个字符串常量就会被认为是无用的,称为废弃常量。

当JVM执行垃圾回收时,它会检查字符串常量池中的所有字符串常量,如果发现某个字符串常量没有任何对象引用它,就会将它从常量池中清除。

我们无法直接判断一个字符串常量是否是废弃常量。只有当JVM执行垃圾回收时,才会对所有字符串常量进行检查和清理。

如何判断一个类是无用的类?

判断一个类是否是无用的类,也就是判断一个类是否可以被垃圾回收器回收,可以通过以下两个角度:

  1. 对象引用:如果一个类的所有实例对象都没有被任何其他对象引用,那么这个类就是无用的类。这种情况通常发生在类实例对象被创建后,没有被赋值给任何变量,也没有被存储在任何集合中,甚至没有被用作方法返回值等情况。
  2. 类加载器:如果一个类的类加载器已经被回收,那么这个类就是无用的类。在Java中,类加载器负责将类的字节码文件加载到JVM中,并将其转换成可执行的Java类。如果类加载器已经被回收,那么这个类就无法再次被加载,也就没有存在的必要了。

在实际开发中,我们无法直接判断一个类是否为无用的类。JVM会在垃圾回收时自动判断类是否为无用的类,并对其进行回收。但是,我们可以通过一些手段来促使JVM更早地回收无用的类,例如关闭某个类所在的ClassLoader,或者强制将某个类的实例置为null。

垃圾收集算法

标记-清除算法

标记清除算法分为两个阶段:标记和清除阶段。首先标记所有不需要回收的类,标记完成后,统一回收没有被标记的类。

标记清除算法的主要优点在于可以处理任意的内存分配模式,而不会浪费任何内存空间。但是,它也存在一些缺点,例如容易产生内存碎片,导致内存利用率降低。

标记-复制算法

这是一种将内存空间分成两块的算法,每次只使用其中一块。当这一块内存用完后,将其中还存活的对象复制到另一块空间中,然后清除原来的内存空间。

复制算法的主要优点在于可以避免内存碎片的产生,并且不需要进行标记和清除的操作。但是,它也存在一些缺点,例如需要耗费额外的空间来存储复制后的对象,以及复制对象的操作可能会影响程序的性能。

标记-整理算法

标记整理算法是一种结合了标记清除算法和复制算法的算法。首先,标记整理算法会遍历所有的存活对象,并标记这些对象。然后,它会将所有的存活对象向一端移动,然后将这一端的空闲内存空间清空。

标记整理算法的主要优点在于可以避免内存碎片的产生,同时也不需要耗费额外的空间来存储复制后的对象。但是,它也存在一些缺点,例如需要对存活对象进行移动操作,可能会影响程序的性能。

分代收集算法

当前虚拟机的垃圾收集都采用分代收集算法,一般将 java 堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。

在Java中,分代收集算法主要分为两代:年轻代和老年代。年轻代是指存活时间较短的对象,通常采用复制算法进行垃圾回收;老年代是指存活时间较长的对象,通常采用标记整理算法进行垃圾回收。

分代收集算法的主要优点在于可以更加高效地进行内存管理,并且可以根据对象的存活时间采用不同的垃圾收集算法。但是,它也存在一些缺点,例如需要额外的空间来存储对象代的信息。

垃圾收集器

Serial 收集器

Serial(串行)收集器是最基本、历史最悠久的垃圾收集器了。大家看名字就知道这个收集器是一个单线程收集器了。它的 “单线程” 的意义不仅仅意味着它只会使用一条垃圾收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集工作的时候必须暂停其他所有的工作线程( “Stop The World” ),直到它收集结束。

新生代采用标记-复制算法,老年代采用标记-整理算法。

ParNew 收集器

ParNew 收集器其实就是 Serial 收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和 Serial 收集器完全一样。

新生代采用标记-复制算法,老年代采用标记-整理算法。

CMS 收集器

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它非常符合在注重用户体验的应用上使用。

CMS(Concurrent Mark Sweep)收集器是 HotSpot 虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。

G1 收集器

G1 (Garbage-First) 是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器. 以极高概率满足 GC 停顿时间要求的同时,还具备高吞吐量性能特征.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sivan_Xin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值