Java垃圾回收算法

Java语言相对于其他语言最大的一个特点就是引入了垃圾回收机制,这样程序员在编写程序的时候不用再考虑申请或者释放内存,极大的增加了开发者的开发效率。虽然垃圾回收是由JVM虚拟机负责完成的,但了解虚拟机的垃圾回收算法有利于编写更高效的代码。


一、如何判断一个对象是否应该被回收

1. 引用计数法(Reference Counting)

引用计数是垃圾收集器中的早期策略,在这种方法中,堆中每个对象实例都有一个引用计数。当一个对象被创建时,且将该对象实例分配给一个变量,该变量计数设置为1。当任何其它变量被赋值为这个对象的引用时,计数加1(a=b,则b引用的对象实例的计数器+1),但当一个对象实例的某个引用超过了生命周期或者被设置为一个新值时,对象实例的引用计数器减1。当一个对象实例被垃圾收集时,它引用的任何对象实例的引用计数器减1,任何引用计数器为0的对象实例可以被当作垃圾收集。
优点:实现简单,判断效率高
缺点:很难解决对象之间的相互循环引用(objA.instance = objB; objB.instance = objA)的问题,所以java语言并没有选用引用计数法管理内存

2. 可达性分析算法(GC Root Tracing)

根搜索算法是从离散数学中的图论引入的,程序把所有的引用关系看作一张图,从一个节点GC ROOT开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点,这些无用不可达的节点就会被当作垃圾处理。由于引用计数法存在互相引用导致无法进行GC的问题,所以目前JVM虚拟机多使用对象可达性分析算法。

java中可作为GC Root的对象有

  • 虚拟机栈中引用的对象(本地变量表)
  • 方法区中静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中引用的对象(Native对象)

二、垃圾回收算法

1. 标记-清除算法(Mark-Sweep)

标记-清除算法采用从根集合进行扫描,对存活的对象对象标记,标记完毕后,再扫描整个空间中未被标记的对象,进行回收。标记-清除算法不需要进行对象的移动,并且仅对不存活的对象进行处理,在存活对象比较多的情况下极为高效,但由于标记-清除算法直接回收不存活的对象,因此会造成内存碎片。
缺点:标记清除后会产生大量的不连续内存碎片,内存碎片过多可能会导致程序需要分配较大对象时找不到足够大的连续内存空间而不得不提前触发另一次垃圾回收动作。

2. 复制算法(Compacting Collector)

复制算法的提出是为了克服句柄的开销和解决堆碎片的垃圾回收。它开始时把堆分成 一个对象面和多个空闲面,程序从对象面为对象分配空间,当对象满了,基于copying算法的垃圾收集就从根集中扫描活动对象,并将每个活动对象复制到空闲面(使得活动对象所占的内存之间没有空闲洞),这样空闲面变成了对象面,原来的对象面变成了空闲面,程序会在新的对象面中分配内存。一种典型的基于coping算法的垃圾回收是stop-and-copy算法,它将堆分成对象面和空闲区域面,在对象面与空闲区域面的切换过程中,程序暂停执行。
优点:每次只对其中一块进行GC,不用考虑内存碎片的问题,并且实现简单,运行高效
缺点:内存缩小了一半

3. 标记-整理算法(Mark-Compact)

标记-整理算法采用标记-清除算法一样的方式进行对象的标记,但在清除时不同,在回收不存活的对象占用的空间后,会将所有的存活对象往左端空闲空间移动,并更新对应的指针。标记-整理算法是在标记-清除算法的基础上,又进行了对象的移动,因此成本更高,但是却解决了内存碎片的问题。在基于Compacting算法的收集器的实现中,一般增加句柄和句柄表。

4. 分代收集算法(Generational Collection)

分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的回收算法,以便提高回收效率。根据对象的存活周期的不同将内存划分为几块,一般就分为新生代和老年代,根据各个年代的特点采用不同的收集算法。新生代(少量存活)用复制算法,老年代(对象存活率高)“标记-清理”算法。

整个JVM内存总共划分为三代:年轻代(Young Generation)、年老代(Old Generation)、持久代(Permanent Generation)。

  • 年轻代:所有新生成的对象首先都放在年轻代内存中。年轻代的目标就是尽可能快速的手机掉那些生命周期短的对象。
  • 年老代:在年轻代经历了N次GC后,仍然存活的对象,就会被放在老年代中。因此可以认为老年代存放的都是一些生命周期较长的对象。
  • 持久代:基本固定不变,用于存放静态文件,例如Java类和方法。持久代对GC没有显著的影响。

三、垃圾收集器

新生代收集器使用的收集器:Serial、PraNew、Parallel Scavenge
老年代收集器使用的收集器:Serial Old、Parallel Old、CMS

各种收集器的特点如下所示:

1. Serial收集器(复制算法)

Serial收集器是一个单线程收集器,并且在它进行垃圾收集时,必须暂停所有用户线程。Serial收集器是针对新生代的收集器,采用的是Copying算法。它的优点是实现简单高效,但是缺点是会给用户带来停顿。

2. Serial Old收集器(标记-整理算法)

Serial收集器的老年代版本,Serial Old收集器是针对老年代的收集器,采用的是Mark-Compact算法,优点是简单高效。

3. ParNew收集器(停止-复制算法)

ParNew收集器是新生代收集器,可以认为是Serial收集器的多线程版本,在多核CPU环境下有着比Serial更好的表现。

4. Parallel Scavenge收集器(停止-复制算法)

Parallel Scavenge收集器是一个新生代的多线程收集器(并行收集器),它在回收期间不需要暂停其他用户线程,其采用的是Copying算法,该收集器与前两个收集器有所不同,它主要是为了达到一个可控的吞吐量。

5. Parallel Old收集器(停止-复制算法)

Parallel Old是Parallel Scavenge收集器的老年代版本(并行收集器),使用多线程和Mark-Compact算法。

6. CMS(Concurrent Mark Sweep)收集器(标记-清理算法)

CMS(Current Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,高并发、低停顿,追求最短GC回收停顿时间,cpu占用比较高,响应时间快,停顿时间短,多核cpu追求高响应时间的选择。

7. G1收集器

G1收集器是当今收集器技术发展最前沿的成果,它是一款面向服务端应用的收集器,它能充分利用多CPU、多核环境。因此它是一款并行与并发收集器,并且它能建立可预测的停顿时间模型。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java垃圾回收算法主要基于可达性分析和标记-清除两种算法。下面是对这两种算法的简要说明: 1. 可达性分析 (Reachability Analysis):这是Java垃圾回收的基础算法。它通过判断对象是否可以从根对象(如线程栈、静态变量等)访问到来确定对象的存活状态。如果一个对象不可达,则认为它是垃圾,可以被回收。 2. 标记-清除 (Mark and Sweep):这是最基本的垃圾回收算法之一。在标记阶段,垃圾回收器从根对象开始遍历所有可达对象,并将其标记为“存活”。在清除阶段,垃圾回收器清除所有未被标记的对象,并回收它们所占用的内存空间。 除了标记-清除算法Java还使用了其他一些高级的垃圾回收算法,包括: 1. 复制算法 (Copying Algorithm):将堆内存分为两个区域,每次只使用其中一个区域。当一个区域满了之后,将存活的对象复制到另一个区域中,并清除当前区域中的所有对象。 2. 标记-整理 (Mark and Compact):类似于标记-清除算法,但在清除阶段之后,它会将存活的对象移动到内存的一端,以便于分配连续的内存空间。 3. 分代算法 (Generational Algorithm):根据对象的存活时间将堆内存划分为不同的代。通常情况下,新创建的对象会被分配到年轻代,而存活时间较长的对象则会被转移到老年代。不同代使用不同的垃圾回收算法进行回收。 这些算法的选择取决于具体的应用场景和性能需求,Java垃圾回收器通常会根据当前堆内存的使用情况和对象的存活特性来选择合适的回收算法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值