Java虚拟机学习11 | 垃圾回收(上)

https://time.geekbang.org/column/article/13091

垃圾回收:将已经分配出去,但现在不再使用的内存回收,以便再次分配,在Java虚拟机环境下,垃圾是指那些死亡对象占据的堆空间

回收算法

引用计数法

引用计数法在每个对象中添加一个引用计数器,用来计算引用该对象的个数,当引用计数器归零时表明该对象已经死亡,可以被回收了.

但是当两个对象相互引用时,类似构成死锁时,两个对象实际上已经死了,但是引用计数器没有归零,因此这两个对象将没有被回收

可达性分析

可达性分析算法是将一系列GC Roots 作为初始的存活对象集,从GC Roots出发探索能够被引用的对象加入存活对象集,而没有被引用的对象将会被标记,第一次标记时是死缓,如果在第二次标记时执行了finalize()方法,那么就逃脱死亡,否则就被回收.

初始的GC Root一般有以下:

  1. 虚拟机栈上的本地变量表引用的对象
  2. 方法区中类的静态属性引用的对象
  3. 方法区中常量引用的对象
  4. 本地方法栈中JNI引用的对象

可达性分析可以解决两个对象相互引用的问题,只要这两个对象不被GC Roots探索到就将被认为死亡.

Stop-the-world以及安全点

在多线程环境下,其中某一个线程对一个对象进行标记,随后另一个线程又对这个对象进行引用,而此时并没有执行finalize()方法.于是一个正在被引用的对象被回收了.

为了解决这个问题,Java虚拟机采用了Stop-the-world方法,就是将其他线程都全部停止,指导垃圾回收完成.这个过程的时间称为暂停时间(GC pause)

Java虚拟机通过安全点(safepoint)机制来实现Stop-the-world.当Java虚拟机收到Stop-the-world请求时,便会等待其他线程全部进入安全点,然后开始执行垃圾回收

Java线程有以下几种状态属于安全点:

  • 执行JNI本地代码
  • 解释执行字节码
  • 执行即时编译器生成机器码
  • 线程阻塞

垃圾回收的三种方式

第一种是清除:将死亡对象占用的内存标记为空闲,并记录到空闲列表(free list)中,当需要新建对象时,内存管理模块会从空闲列表中寻找空闲内存,并分配给新建对象

清除这种方式会造成内存碎片,而Java的堆中的对象要求连续存储,所以有可能有空闲的内存空间,但是没有可分配的内存. 还有一个缺点是分配的效率慢,当一个连续内存时,可以通过内存指针直接分配空间,但是空闲列表需要遍历查找

第二种是压缩:这种做法是将存活对象后聚集到内存区域的起始位置,这样能够解决内存碎片,但是有比较大的内存开销

第三种是复制:将内存空间两等分,分别用from指针和to指针管理,当垃圾回收时,将存活对象复制到to区域,并且交换from和to的指向区域,这样也能解决内存碎片,但是空间利用率太低了

猜你喜欢

转载自blog.csdn.net/qq_34332035/article/details/88260292