JVM—垃圾收集器

JVM---垃圾收集器

什么是垃圾

没有被引用的对象就是垃圾。

怎么找到垃圾

引用计数法

当对象引用消失,对象就称为垃圾。

对象消失一个引用,计数减去一,当引用都消失了,计数就会变为0.此时这个对象就会变成垃圾。

在堆内存中主要的引用关系有如下三种:单一引用、循环引用、无引用:

引用计数法性能比较低。

引用计数算法不能解决循环引用问题,为了解决这个问题,JVM使用了根可达分析算法。

根可达算法

根可达算法的基本思路就是通过一系列的名为GCRoot的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GCRoot没有任何引用链相连时,则证明此对象是不可用的,也就是不可达的。

可作GCRoots的对象:

  • 虚拟机栈中,栈帧的本地变量表引用的对象。
  • 方法区中,类静态属性引用的对象。
  • 方法区中,常量引用的对象。
  • 本地方法栈中,JNI引用的对象。

回收过程

即使在可达性分析算法中不可达的对象,也并非是"非死不可"。被判定不可达的对象处于"缓刑"阶段。要真正宣告死亡,至少要经历两次标记过程:

  • 第一次标记:如果对象可达性分析后,发现没有与GC Roots相连接的引用链,那它将会被第一次标记;
  • 第二次标记:第一次标记后,接着会进行一次筛选。筛选条件:此对象是否有必要执行finalize()方法。在finalize()方法中没有重新与引用链建立关联关系的,将被进行第二次标记。

第二次标记成功的对象将真的会被回收,如果失败则继续存活。

对象引用

在JDK1.2之后,Java对引用的概念进行了扩充,将引用分为强引用(StrongReference)、软引用(SoftReference)、弱引用(WeakReference)、虚引用(PhantomReference)四种,这四种引用强度依次逐渐减弱。

引用类型 被垃圾回收的时间 用途 生存时间
强引用 从来不会 对象的一般状态 JVM停止时终止
软引用 内存不足时 对象缓存 内存不足时终止
弱引用 正常GC 对象缓存 GC后终止
虚引用 正常GC 类似事件回调机制 GC后终止
无引用 正常GC 对象的一般状态 GC后终止

强引用

代码中普遍存在,只要强引用还在,就不会被GC。

java 复制代码
Object obj = new Object();

软引用

非必须引用,内存溢出之前进行回收,如内存还不够,才会抛异常。

java 复制代码
Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null;
Object o = sf.get();//有时会返回null
System.out.println("o = " + o);

应用场景:软引用可用来实现内存敏感的高速缓存。

举例如下:

  • 应用需要读取大量本地文件,如果每次读取都从硬盘读取会严重影响性能,如果一次性全部加载到内存,内存可能会溢出。
  • 可以使用软引用解决这个问题,使用一个HashMap来保存文件路径和文件对象管理的软引用之间的映射关系。
  • 内存不足时,JVM会自动回收缓存文件对象的占用空间,有效地避免了OOM问题。

弱引用

非必须引用,只要有GC,就会被回收。

java 复制代码
Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
obj = null;
//System.gc();
Object o = wf.get();//有时候会返回null
boolean enqueued = wf.isEnqueued();//返回是否被垃圾回收器标记为即将回收的垃圾
System.out.println("o = " + o);
System.out.println("enqueued = " + enqueued);
  • 弱引用是在第二次垃圾回收时回收,短时间内通过弱引用取对应的数据,可以取到,当执行过第二次垃圾回收时,将返回null。
  • 作用:监控对象是否已经被垃圾回收器标记为即将回收的垃圾,可以通过弱引用的isEnQueued方法返回对象是否被垃圾回收器标记。

虚引用

  • 虚引用是最弱的一种引用关系。垃圾回收时直接回收
  • 一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。
java 复制代码
Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj, new
                                                           ReferenceQueue<>());
obj=null;
Object o = pf.get();//永远返回null
boolean enqueued = pf.isEnqueued();//返回是否从内存中已经删除
System.out.println("o = " + o);
System.out.println("enqueued = " + enqueued);
  • 虚引用是每次垃圾回收的时候都会被回收,通过虚引用的get方法永远获取到的数据为null,因此也被成为幽灵引用。
  • 作用:跟踪戏象被垃圾回收的状态,仅仅是提供一种确保对象被回收后,做某些事情的机制。类似事件监听机制

如何清除垃圾

JVM提供3种方法,清除垃圾对象:

  • Mark-Sweep标记清除算法
  • Copyirvg拷贝算法
  • Mark-Compact标记压缩算法

标记清除算法(Mark-Sweep)

最基本的算法,主要分为标记和清除两个阶段。首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。

缺点:

  • 效率不高,标记和清除过程的效率都不高;
  • 空间碎片,会产生大量不连续的内存碎片,会导致大对象可能无法分配,提前触发GC。

拷贝算法(Copying)

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

一般采用这种收集算法来回收新生代,当回收时,将Eden和Survivor中还存活着的对象拷贝到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor的空间。

HotSpot虚拟机默认Eden和Survivor的大小比例是8:1,也就是每次新生代中可用内存空间为整个新生代容量的90%(80%+10%),只有10%的内存是会被"浪费"的。当Survivor空间不够用时,需要依赖其他内存(这里指老年代)进行分配担保(Handle Promotion)。

  • 优点:没有碎片化,所有的有用的空间都连接在一起,所有的空闲空间都连接在一起;
  • 缺点:存在空间浪费;

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

老年代没有人担保,不能用复制回收算法。可以用标记-整理算法,标记过程仍然与"标记-清除"算法一样,然后让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

缺点:性能较低,因为除了拷贝对象以外,还需要对象内存空间进行压缩,所以性能较低。

分代回收(Generational Collection)

当前商业虚拟机都是采用这种算法。根据对象的存活周期的不同将内存划分为几块。

  • 新生代,每次垃圾回收都有大量对象失去,选择复制算法。
  • 老年代,对象存活率高,无人进行分配担保,就必须采用标记清除或者标记整理算法。

用什么清除垃圾

有 8 种不同的垃圾回收器,它们分别用于不同分代的垃圾回收。

  • 新生代回收器:Serial、ParNew、Parallel Scavenge
  • 老年代回收器:Serial Old、Parallel Old、CMS
  • 整堆回收器:G1、ZGC
相关推荐
学点东西吧.7 分钟前
JVM(五、垃圾回收器)
jvm
请你打开电视看看3 小时前
Jvm知识点
jvm
程序猿进阶3 小时前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
阿龟在奔跑15 小时前
引用类型的局部变量线程安全问题分析——以多线程对方法局部变量List类型对象实例的add、remove操作为例
java·jvm·安全·list
王佑辉15 小时前
【jvm】方法区常用参数有哪些
jvm
王佑辉15 小时前
【jvm】HotSpot中方法区的演进
jvm
Domain-zhuo16 小时前
什么是JavaScript原型链?
开发语言·前端·javascript·jvm·ecmascript·原型模式
Theodore_10222 天前
7 设计模式原则之合成复用原则
java·开发语言·jvm·设计模式·java-ee·合成复用原则
我是苏苏2 天前
Web开发:ORM框架之使用Freesql的DbFrist封装常见功能
java·前端·jvm
天草二十六_简村人2 天前
Java语言编程,通过阿里云mongo数据库监控实现数据库的连接池优化
java·jvm·数据库·mongodb·阿里云·微服务·云计算