JVM内存回收机制

如何判断一个对象可回收

引用计数法

为每一个对象设置一个计数器,计数其他地方对其的引用,任何时刻计数器为0的对象就是不可能再被引用的,可以被回收。但是,这种简单的方法会导致一个问题,无法回收存在相互循环引用的对象,因此,目前主流的java虚拟机中没有选用此法来管理内存的。

可达性分析

在主流的内存管理实现中,基本都是使用可达性分析来判断一个对象是否存活的。这种实现主要是通过设置一系列的“GC roots”对象,从这些对象为起始点开始往下搜索,所走过的路径称为“引用链”,当一个对象没有“GC roots”到达它时,此对象为不可达的,可以被回收。

四种引用

强引用:类似Object object= new Object()这类引用,只要强引用还存在,GC永远不会回收被引用的对象。
软引用:当引用对象无用时,若内存中有足够空间,则不回收软引用对象,若内存空间不足,则回收时软引用对象会被回收。
弱引用:弱引用是比软引用还要弱的引用,弱引用对象只能活过一次GC,当下一次GC到来时,不论被引用对象有无用,都会被回收。
虚引用:虚引用又称虚幻引用或幽灵引用,使用此引用的唯一作用就是在对象被回收时收到提示。

回收前自救

JVM在执行GC过程的时候,会进行两次标记。当进行可达性分析后,对不可达对象进行第一次标记,并且进行筛选,筛选条件是对象是否需要执行finalize()方法。当对象没有覆写finalize()方法,或者finalize()方法已被虚拟机执行过,则判为“无需执行”。
虚拟机将需要执行finalize()方法的对象放入到一个F-QUEUE等待回收,此时,若对象能再次与引用链挂上钩,比如将自己赋值给某一个变量,则在GC进行第二次标记时,此对象会被移出队列,不会被回收。需要注意的是,对于同一个对象,系统只会自动调用一次finalize()(若其有覆写),第二次回收时,对象若无被引用,则一定会被回收,无法使用这种方法再次进行自救。

回收算法

标记-清除算法

这种算法是最基础最简单的算法,后面的几个内存回收算法也是在此方法上改进而来。首先将要被回收的内存块进行标记,然后将被标记的内存块直接清除,但是使用这种方法会带来两个问题,一个是效率问题,一个是空间问题。效率问题是标记和清除两个过程效率都不高。空间问题是使用这种方法进行内存回收后会留下大量内存碎片,当之后要为大对象分配空间时会因为找不到空间而提前触发一次GC操作。
在这里插入图片描述

复制算法

为了解决效率问题,复制算法出现。这种算法将内存分为大小相同的两个块,每次只使用其中一块。当要执行GC时,将内存中存活下来的对象复制到内存的另一块,然后将之前的内存全部清除。但是这种算法每次只能使用一半大小的内存,代价实在是太大了。
在这里插入图片描述

Eden和Survivor

根据IBM公司的研究,98%的对象都是“朝生夕死”的,所以不需要1:1的比例,而是将内存分为一块Eden和两块Survivor,Eden和Survivor的大小比例为8:1,每次使用一块Eden和一块Survivor,也就是说,每次新生代的可用内存占整个新生代的90%。

标记-整理算法

当对象存活率较高时,复制算法的效率就会变低,因此,在老年代中肯定不适合使用复制算法。标记-整理算法与标记-清除算法其实差不多,只是后者在第二步将所有存活的对象向一端移动,然后再清除边界之外的内存。此算法可解决内存碎片问题。
在这里插入图片描述

安全点

HotSpot虚拟机将所有的对象的引用存在了OopMap这个数据结构中,并且,只在特定位置会生成OopMap,这些位置称为安全点。程序并不是在所有地方都能停下来GC,只有在到达安全点时才能GC。

安全区

安全点机制保证了程序在执行时,在不太长的时间就会遇到可进入GC的安全点,但是当程序sleep或者blocked时,是无法“走”到安全点去挂起的,这时候就需要引入安全区来解决。安全区是指在一段代码片段之中,引用关系不会发生变化,在这个区域的所有地方开始GC都是安全的,可以看做是扩展了的安全点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值