位置:首页 > 后端 > java

Java虚拟机(3)JVM GC垃圾回收机制

chenlong 发布:2021-08-28 22:23:04阅读:

1、Java堆

GC主要发生在堆内存中,所以此处会会对堆内存进行比较详细的描述。

堆内存是由存活和死亡的对象组成的。存活的对象是应用可以访问的,不会被垃圾回收。死亡的对象是应用不可访问尚且还没有被垃圾收集器回收掉的对象。一直到垃圾收集器把这些对象回收掉之前,他们会一直占据堆内存空间。堆是应用程序在运行期请求操作系统分配给自己的向高地址扩展的数据结构,是不连续的内存区域。用一句话总结堆的作用: 程序运行时动态申请某个大小的内存空间 。

层次结构 如下:

(一)堆内存

(1)新生代

  1. Eden(占比80%)

  2. 两块survivor space(占比20%)

(2)老年代

(二)非堆内存

新生代 :刚刚新建的对象在Eden中,经历一次Minor GC,Eden中的存活对象就会被移动到第一块survivor space S0,Eden被清空;等Eden区再满了,就再触发一次Minor GC,Eden和S0中的存活对象又会被复制送入第二块survivor space S1。S0和Eden被清空,然后下一轮S0与S1交换角色,如此循环往复。如果对象的复制次数达到16次,该对象就会被送到老年代中。

老年代 :如果某个对象经历了几次垃圾回收之后还存活,就会被存放到老年代中。老年代的空间一般比新生代大。

Minor GC 发生在新生代,频率高,速度快(大部分对象活不过一次Minor GC)

Major GC 发生在老年代,速度慢

Full GC 清理整个堆空间

不过实际运行中,Major GC会伴随至少一次 Minor GC,因此也不必过多纠结于到底是哪种GC(在有些资料中看到把full GC和Minor GC等价的说法)。

那么, 当我们创建一个对象后,它会被放在堆内存的哪个部分呢?

当我们创建一个对象首先会在Eden初始化内存,如果Eden足够则申请结束,如果Eden不足则释放Eden中不活跃对象,如果释放完以后足够了则申请结束,如果释放完以后还是不够则将部分Eden活跃对象放入survivor,然后放入老年代中,如果老年代空间不够则保留在survivor,在老年代进行Major.GC然后放入老年代中,Major GC之后还是老年代不足,那就没办法了,JVM会抛出内存不足的异常。

2、垃圾回收机制 

JAVA 并没有给我们提供明确的代码来标注一块内存并将其回收。或许你会说,我们可以将相关对象设为 null 或者用 System.gc()。然而,后者将会严重影响代码的性能,因为一般每一次显式的调用 system.gc() 都会停止所有响应,去检查内存中是否有可回收的对象。这会对程序的正常运行造成极大的威胁。另外,调用该方法并不能保证 JVM 立即进行垃圾回收,仅仅是通知 JVM 要进行垃圾回收了,具体回收与否完全由 JVM 决定。这样做是费力不讨好。

垃圾回收器是利用有向图来记录和管理内存中的所有对象,通过该有向图,就可以识别哪些对象“可达”,哪些对象“不可达”,“不可达”的对象就是可以被回收的。这里举一个很简单的例子来说明这个原理:

public class Test{  public static void main(String[] a){     Integer n1=new Integer(9);     Integer n2=new Integer(3);     n2=n1;     // other codes  }}


3、 垃圾回收算法概述

  1. 引用计数算法

给对象中添加一个引用计数器,每当有一个地方引用它时,计数器的值加1;当引用失效时,计数器值就减1;任何时刻计算器为0的对象就是不可能再被使用的。

  1. 追踪回收算法(tracing collector)

从根结点开始遍历对象的应用图。同时标记遍历到的对象。遍历完成后,没有被标记的对象就是目前未被引用,可以被回收。

  1. 压缩回收算法(Compacting Collector)

把堆中活动的对象集中移动到堆的一端,就会在堆的另一端流出很大的空闲区域。这种处理简化了消除碎片的工作,但可能带来性能的损失。

  1. 复制回收算法(Coping Collector)

把堆均分成两个大小相同的区域,只使用其中的一个区域,直到该区域消耗完。此时垃圾回收器终端程序的执行,通过遍历把所有活动的对象复制到另一个区域,复制过程中它们是紧挨着布置的,这样也可以达到消除内存碎片的目的。复制结束后程序会继续运行,直到该区域被用完。 但是,这种方法有两个缺陷:

(1)对于指定大小的堆,需要两倍大小的内存空间,

(2)需要中断正在执行的程序,降低了执行效率

  1. 按代回收算法(Generational Collector) 为什么要按代进行回收?这是因为不同对象生命周期不同,每次回收都要遍历所有存活对象,对于整个堆内存进行回收无疑浪费了大量时间,对症下药可以提高垃圾回收的效率。主要思路是:把堆分成若搞个子堆,每个子堆视为一代,算法在运行的过程中优先收集“年幼”的对象,如果某个对象经过多次回收仍然“存活”,就移动到高一级的堆,减少对其扫描次数。

4、垃圾回收器

  1. 串行回收器(serial collector)

客户端模式的默认回收器,所谓的串行,指的就是单线程回收,回收时将会暂停所有应用线程的执行

  1. 并行回收器

服务器模式的默认回收器,利用多个线程进行垃圾回收,充分利用CPU,回收期间暂停所有应用线程

  1. CMS回收器

停顿时间最短,分为以下步骤:1初始标记;2并发标记;3重新标记;4并发清除。优点是停顿时间短,并发回收,缺点是无法处理浮动垃圾,而且会导致空间碎片产生

  1. G1回收器

新技术,将堆内存划分为多个等大的区域,按照每个区域进行回收。工作过程是1初始标记;2并发标记;3最终标记;4筛选回收。特点是并行并发,分代收集,不会导致空间碎片,也可以由编程者自主确定停顿时间上限


24人点赞 返回栏目 提问 分享一波

小礼物走一波,支持作者

还没有人赞赏,支持一波吧

留言(问题紧急可添加微信 xxl18963067593) 评论仅代表网友个人 留言列表

暂无留言,快来抢沙发吧!

本刊热文
网友在读
手机扫码查看 手机扫码查看