3 JVM怎么知道哪些对象需要gc

前面我们讲了JVM的堆内存的管理方式,虽然没有把整体的细节讲完。但是也介绍了参考书籍,有兴趣的朋友可以参看源码。

我们知道了jvm管理内存,那么JVM怎么知道哪个对象需要释放的呢?

我们现在讲下JVM回收对象的几种方式:

1:引用计数

该算法在对象中添加一个引用计数器,每次该对象被引用时,计数器的值就会加1,每当引用失效时计数器就会减1,当计数器的值为0时,该对象就没有在被使用了。

引用计数的算法虽然占用额外的内存开销,但原理简单效率高,现在的计算机也不缺内存。
但是目前主流的java虚拟机并不采用该算法,为什么呢?主要是因为很难解决对象之间循环引用的问题。
例如A对象引用了B对象,B对象同时也引用了A对象,

public class MyObject {
    public Object ref = null;
    public static void main(String[] args) {
        MyObject o1= new MyObject();
        MyObject o2= new MyObject();
        o1.ref = myObject2;
        o2.ref = myObject1;
        o1= null;
        o2= null;
    }
}

我们虽然设置o1 o2为null但是并没有影响到成员变量ref,他们之间相互引用,引用计数不能变为0.所以这两个对象并不能被释放。

2:可达性分析算法

hotspot就是采用这种算法判断对象是否需要释放

这种算法的基本思想是通过GC roots的根对象作为起点,从这些节点开始,根据引用关系向下搜索。

搜索的过程有个引用链,如果某个对象到gc roots间没有引用链,或者说到gc roots 不可达则证明此对象不可能再被使用

在jvm中固定可认为是gc roots的对象包括以下几种:

  1. 在虚拟机栈(栈中 的本地变量表)中引用的对象,例如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。
  2. 在方法区中常量引用的对象,例如字符串常量池(String Table)里的引用。
  3. 在本地方法栈中JNI(本地方法)引用的对象。
  4. Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(NullPointException、OutOfMemoryError)等,以及系统类加载器。
  5. 所有被同步锁(synchronized)持有的对象。
  6. 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等
3:引用类型

java将引用分为强引用、软引用、弱引用、和虚引用

强引用内存不足也不会回收,软引用内存不足才回收,弱引用和虚引用看见就回收。

4:垃圾收集算法

垃圾收集(Garbage Collection,GC)算法是Java虚拟机(JVM)用来自动管理内存的一种方式。主要的目标是找出那些已经不再使用的对象,并释放它们所占用的内存空间

4.1 标记-清除算法

标记清除是最早也是最基础的垃圾回收算法

它分为「标记」和「清除」两个阶段:首先标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象,也可以反过来,标记存活的对象,统一回收所有未被标记的对象

优点: 不需要进行对象的移动,在存活对象比较多的情况下非常高效
缺点:第一个是执行效率不稳定,如果Java堆中包含大量对象,而且其中大部分是需要被回收的,这时必须进行大量标记和清除的动作。
第二个是内存空间的碎片化问题,标记、清除之后会产生大量不连续的「内存碎片」,而内存碎片是无法被分配对象的,内存碎片太多可能会导致当以后在程序运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

4.2 标记复制算法

标记复制算法将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。

如果内存中多数对象都是存活的,这种算法将会产生大量的内存间复制的开销,但对于多数对象都是可回收的情况,算法需要复制的就是占少数的存活对象,而且每次都是针对整个半区进行内存回收,分配内存时也就不用考虑有空间碎片的复杂情况。

所以,标记-复制算法通常用在新生代的Eden区和Survivor区,这两个区的对象,朝生夕死,多数对象都是可回收的。

优点:解决了标记清除算法中内存碎片的问题
缺点:代价是将可用内存缩小为了原来的一半,并且在对象存活率较高时就要进行较多的复制操作,效率将会降低。

4.3 标记整理算法

算法在对象存活率较高时就要进行较多的复制操作,效率将会降低。

更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况。

所以在老年代一般不能直接选用这种算法。针对老年代对象的存亡特征,1974年Edward Lueders提出了另外一种有针对性的标记-整理(Mark-Compact)算法。

其中的标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存。

优点:经过整理之后,新对象的分配只需要通过指针碰撞便能完成,也解决了内存碎片的问题。
缺点:GC 暂停的时间会增长,对象移动的时间成本是十分可观的。 这也就是full gc 为什么那么耗时的原因

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值