首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Java虚拟机入门--垃圾回收简述

Java虚拟机入门--垃圾回收简述

作者头像
终码一生
发布2022-04-14 08:26:33
发布2022-04-14 08:26:33
3510
举报
文章被收录于专栏:终码一生终码一生

解决一个复杂问题的同时,同时会引入一个更大的问题 -- by 我自己

Java虚拟机垃圾回收机制开启了垃圾自动回收之路,同样怎样自动化回收垃圾,转换为怎样才能把垃圾回收的又快又好。

那么先来看看JVM怎么判定哪些东西该回收,回收算法怎么做的。

目前JVM使用可达性分析算法,判定哪些对象该回收了。

通过一系列称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,

搜索所走过的路径称为引用链(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连(从 GC Roots 到这个对象不可达)时,则证明此对象是不可用的,它会被判定为是可回收的对象。

在Java语言中,可作为GC Roots的对象包括下面几种:

虚拟机栈(栈帧中的本地变量表)中引用的对象 。

方法区中类静态属性引用的对象。

方法区中常量引用的对象。

本地方法栈中JNI引用的对象。

可以概括为:方法运行时,方法中引用的对象;

类的静态变量引用的对象;

类中常量引用的对象;

Native方法中引用的对象。

可达性分析算法也有一些问题,那就是在多线程环境下可能对多个线程更新对象引用错误更新

比如对象更新一个对象的引用为空,或者把对象标记为没有引用了

那就可能造成一种是 该回收没回收,这个无非就是浪费点儿内存,后面多轮回收可能就回收掉了。

但是不该回收的回收了,那么就是不该拿走的东西拿走了,那可就会造成运行异常了。所以多线程编程需要注意的点还是蛮多的。

有了这些问题,就要有解决问题的方法。Java虚拟机会使用Stop-the-world及安全点检查。

简单点就是,当虚拟机收到Stop-the-world请求,它会等待所有线程都到达安全点,才允许请求Stop-the-world的线程独占工作。

垃圾回收的方式:

当标记完所有的存活对象时,我们便可以进行死亡对象的回收工作了。主流的基础回收方式可分为三种。

第一种是清除(sweep),就是把死亡对象所占据的内存标记为空闲内存,然后记录在一个空闲列表之中。

这些空闲对象的位置都是不确定的,当需要新建对象时,内存管理模块便会从该空闲列表中寻找空闲内存,并划分给新建的对象。

清除这种方式思路比较简单,就是哪里空闲收哪里,很随性,但是有两个缺点。

一是会造成内存碎片。由于 Java 虚拟机的堆中对象必须是连续分布的,因此可能出现总空闲内存足够,但是无法分配的极端情况。

另一个则是分配效率较低。如果是一块连续的内存空间,那么我们可以通过指针加法(pointer bumping)来做分配。

而对于空闲列表,Java 虚拟机则需要逐个访问列表中的项,来查找能够放入新建对象的空闲内存。

第二种是压缩(compact),就是把存活的对象集中存放到内存起始位置,这样就留下连续的内存空间可用。

这种做法解决了内存碎片化的问题,代价就是压缩算法会占用一些性能开销。

第三种则是复制(copy),就是把内存分两等份,相当于两个人搬东西 A 和 B,只有一个人干活,当发生垃圾回收的时候,A就把自己所在区域的存活对象搬运到B的区域,B就是一个在一个地方守着,看货的角色,

搬完后A和B交换身份,B变成了A, 下一次还是A来完成这样的动作,周而往复。

复制解决了内存碎片化的问题,但是它的缺点也极其明显,干一件事儿要两个人或两块儿地儿,本来一块儿区域能解决的问题,要两个区域才能完成,即堆空间的使用效率极其低下。

当然,现代的垃圾回收器往往会综合上述几种回收方式,综合它们优点的同时规避它们的缺点。

今天介绍了一些垃圾回收的基础知识,后续可以探讨一下回收算法的实现

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-11-05,如有侵权请联系 cloudcommunity@tencent.com 删除
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档