程序员的视角:java GC



GC(Garbage Collection 垃圾回收)的概念随着 java 的流行而被人们所熟知。 实际 GC 最早起源于20世纪60年代的 LISP 语言,是一种自动的内存管理机制。 GC 要解决的问题有 3 个:

1. 回收什么?(what)

2. 何时回收?(when)

3. 如何回收?(how)

回收什么?

清理的是垃圾,回收的是内存空间。

既然 GC 是 java 的自动内存管理机制,那么先看下 java 虚拟机将所管理的内存划分为不同的区域,如图1。

如图1所示,java 虚拟机管理的内存区域分为如下几个部分:

1. 堆(Heap)

2. 方法区(Method Area)

3. 虚拟机栈(VM Stack)

4. 本地方法栈(Native Method Stack)

5. 程序计数器(Program Counter Register)

其中堆和方法区属于所有线程共享,而其他区域属于线程隔离的区域。

下面我们以 java HotSpot 虚拟机为例分别说说每个区域的作用和构成:

堆(Heap)

堆用于存储对象实例,从内存回收的角度看,由于收集器基本都采用了分代收集算法,所以堆可以进一步细分为:

- Eden 区

- Survivor 0 区 (From)

- Survivor 1 区 (To)

- Old/Tenured 区

其中 Eden、S0、S1 组成了新生代(Young/New Generation),Old/Tenured 为老年代。

方法区(Method Area)

方法区存储虚拟机加载的类信息、常量、编译代码等数据。 HotSpot 虚拟机使用永久代(Permanent Generation)来实现方法区。

虚拟机栈(VM Stack)

虚拟机栈描述的是 java 方法执行的内存模型,每个方法在执行时创建一个栈帧(Stack Frame)。 栈帧中存储内容主要包含:

- 局部变量表

- 操作数栈

- 动态链接

- 方法返回地址

每个方法的执行过程就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

本地方法栈(Native Method Stack)

本地方法栈与虚拟机栈类似,只不过服务于虚拟机执行 Native 方法时。 HotSpot 虚拟机的实现把虚拟机栈和本地方法栈合二为一。

程序计数器(Program Counter Register)

可以看作是线程执行的字节码的行号指示器,在虚拟机的概念模型中便于实现分支、循环、跳转、异常处理和线程切换恢复等基础功能。 每个线程都有一个独立的程序计数器。

GC 管理的内存区域主要是堆(Heap),而堆中存放的是对象实例,因此 GC 回收的就是“死亡”(不可能再被使用)的对象占用的内存空间。

何时回收?

既然说到“死亡”的对象,那不得不说下对象的生命周期。

虚拟机通过 new 指令创建了对象,大多数对象创建时在 Eden 区分配内存空间,而一些大对象若 Eden 区不能满足其空间需求时会直接在 Old/Tenured 区分配。

对象的死亡判定,主流的 GC 实现都是通过可达性分析,形象点来说就是在基于引用建立的对象图中形成了孤岛的对象就是死亡的(可回收的)。

GC 分类

- Minor GC

- Major GC

- Full GC

Minor GC 是针对新生代的回收,当 Eden 区空间满了时将触发 Minor GC。

Major GC 是针对老年代的回收,当 Minor GC 发生时会拷贝对象到老年代,这个过程称为对象晋升(promotion)或老年化(tenuring)。

为了避免对象晋升时老年代空间不足,收集器总是尝试预测剩余的空间是否足够以避免对象晋升失败,当晋升失败时就会发生 Full GC。

Full GC 是针对整个堆的操作,是非常昂贵的操作。除了在对象晋升失败时发生 Full GC,当堆自动调整大小时(Heap-Resizing)也会发生,不过可以通过设置 -Xms和-Xmx为相同的值来避免 Heap-Resizing。


如何回收?

Minor GC 将新生代中存活的对象拷贝到 Survivor 区和 Tenured 区。

Major GC 针对老年代区域进行死亡对象标记、清除和内存整理。

Full GC 则包括了所有存活对象的晋升以及老年代的内存回收及整理。

前面泛泛而谈了3种垃圾收集方式的过程,而具体则是由垃圾收集器来实现。

截至 JDK 1.7 HotSpot 虚拟机提供的垃圾收集器如图2所示,一共有 7 种不同作用的收集器。

图中连线表明它们可以搭配使用。

Serial Collector

如其名,串行的单线程收集器,是目前虚拟机运行在 client 模式下的默认新生代收集器。

ParNew Collector

相当于 Serial 的多线程版本。

Parallel Scavenge Collector

与 ParNew 很像,但它的关注点在达到一个可控制的吞吐量(Throughput),这里吞吐量的定义是 CPU 用于运行用户代码的时间与 CPU总消耗时间的比值。

因此 Parallel Scavenge 收集器也经常称为吞吐优先收集器,它还有个特点是自适应调节策略。 虚拟机会根据当前系统的运行情况收集监控信息,动态调整 Eden与Survivor区比例、晋升老年代对象年龄等参数,以提供最合适的停顿时间或最大的吞吐量。

Serial Old Collector

相当于 Serial 收集器的老年代版本。

Parallel Old Collector

相当于 Parallel Scavenge 收集器的老年代版本。

Concurrent Mark Sweep (CMS) Collector

前述的收集器在执行时都会停止所有的用户线程执行(Stop-The-World)

CMS 收集器的关注点则是尽可能地缩短垃圾收集时用户线程的停顿时间,让垃圾收集和用户线程并行执行,从而减少应用停顿时间,提升用户体验。

当然在获得低停顿的好处时是付出了吞吐量的代价,通常与 Parallel 系收集器相比吞吐率下降 10%-40%。


CMS 收集器的处理整个过程有如下步骤:

1. 初始标记:找到 GC Roots。

2. 并发标记:标记所有从 GC Roots 可达的对象。

3. 并发预清理:检查对象引用更新和在并发标记阶段晋升到老年代的对象并进行标记。

4. 重新标记:标记预清理阶段更新的对象引用。

5. 并发清理:回收死亡对象的内存。

6. 并发重置:重置数据结构为下次运行作准备。

其执行示意如图3所示

其中步骤1(初始标记)和步骤4( 重新标记)仍然需要 Stop The World,只是相对来说时间较短。

低停顿是 CMS 收集器是的优点,但它也并不完美,它有 3 个明显缺点:

1. 由于和用户线程并发执行,所以存在 CPU 争抢的问题。

2. 无法回收浮动垃圾。

3. CMS 仅进行了标记、清除而未进行整理,容易产生大量内存空间碎片。

CMS 默认启动的回收线程是 (CPU数量 + 3) / 4,也就是 CPU 在 4 个以上时并发回收线程使用的 CPU 资源不少于 25%。 在并发清理时新产生的垃圾称为浮动垃圾(Floating Garbage),本次无法收集,当浮动垃圾过多导致预留的内存无法满足程序需要时触发, 就可能出现 Concurrent Mode Failure 导致启用 Serial Old 收集器作为后备进行 Full GC。

Garbage First (G1) Collector

一种新的收集器,在 jdk7u4 开始正式支持,它有如下特点:

1. 多分区的堆组织方式

G1 也是分代收集器,但其组织堆的方式与其他收集器完全不同。它根据不同的用途将堆分为大量(~2000)固定大小的区域(region)。 相同用途的堆也并不连续,G1 依然保留了新生代和老年代的概念,但新生代和老年代不再是物理上隔离的了,它们都是一部分 region 的集合,如图4所示。

如果一个对象大小超过了普通区域大小的50%,那么它会被分配到一个大区域(humongous)里面。

2. 优先的收集方式

G1 的收集方式追求低停顿,并且建立可预测的停顿时间模型(在 M 毫秒的时间片段内,GC 的时间不得超过 N 毫秒,N < M)。 G1 通过有计划的避免在整个堆中进行全区域扫描进行垃圾收集,它通过跟踪各个 region 中垃圾的价值大小(回收获得的空间及回收所花费的时间的经验值), 在后台维护一个优先级列表,每次根据允许的收集时间,优先回收价值最大的 region,这也正式 Garbage-First 名称的由来。 而对 region 的收集采用的是 Stop-The-World 的方式,增量的将存活的对象复制到一个空 region 里面,这种方式不会产生内存碎片问题。

最后我们引用《Java Garbage Collection Distilled》 一文中的关于 GC 的折衷权衡点来总结下。

俗话说:“从来没有不劳而获,当我们得到某些事物的时候,通常不得不放弃另外一些事物”。

当谈论垃圾收集的时候,我们主要考虑三个收集器的指标:

1. 吞吐量:花费在 GC 上的时间占整个应用程序工作的比例。

2. 延迟:因为垃圾回收,而引起的响应暂停的时间。

3. 内存:我系统使用内存来存储状态,在管理的时候它们常常需要复制和移动。

上述三个指标,吞吐量越大越好,延迟越低越好,内存复制和移动产生的碎片越少越好。 但可惜这三个目标很难同时满足,很多时候我们都是根据应用类型在其中做出权衡取舍。


转载于:https://juejin.im/post/5c3483b4518825233b4e66ce

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/252598.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

spring mvc拦截器HandlerInterceptor

本文主要介绍springmvc中的拦截器&#xff0c;包括拦截器定义和的配置&#xff0c;然后演示了一个链式拦截的测试示例&#xff0c;最后通过一个登录认证的例子展示了拦截器的应用 拦截定义 定义拦截器&#xff0c;实现HandlerInterceptor接口。接口中提供三个方法。 public cla…

mysql show 语句大全

mysql show 语句大全 show open tables; 基于本人对MySQL的使用&#xff0c;现将常用的MySQL show 语句列举如下&#xff1a; 1.show databases ; // 显示mysql中所有数据库的名称 2.show tables [from database_name]; // 显示当前数据库中所有表的名称 3.show columns from …

阿里云Aliplayer高级功能介绍(一):视频截图

基本介绍H5 Video是不提供截图的API的&#xff0c; 视频截图需要借助Canvas&#xff0c;通过Canvas提供的drawImage方法&#xff0c;把Video的当前画面渲染到画布上&#xff0c; 最终通过toDataURL方法可以导出图片的base64编码&#xff0c;基本就完成了图片截图的功能。 功能实…

POJ 1151 Atlantis 线段树+扫描线

解题思路: 先将y轴进行离散化。n个矩形的2n个横边纵坐标共构成最多2n-1个区间的边界&#xff0c;对这些区间编号&#xff0c;建立起线段树。 x轴记录左边和右边&#xff0c;左边时是矩形面积增加&#xff0c;覆盖层数增加边&#xff0c;右边是形面积减少&#xff0c;覆盖层数减…

分页

1.首先在数据库中建立一个视图&#xff08;在aspx中sql查询语句是view_student不是student&#xff09;&#xff0c;在视图里创建create view view_student--创建视图as row_number 行号 一条数据是一行 分页功能要根据行数运算select *,row_number() over(order by stuNo desc…

NFS服务端的安装

执行以下四步操作即可完成在虚拟机上安装完成NFS的服务端&#xff1a;第一步&#xff1a;在虚拟机上安装nfs服务&#xff1a; sudo apt install nfs-kernel-server 第二步&#xff1a;修改文件 sudo vi /etc/exports 在文件末尾增加 /home/zzf/hisi-sdk 192.16…

【C++STL/红黑树】POJ 3481 DoubleQueue

POJ 3481 Double Queue 描述&#xff1a; 新成立的BIG-Bank在不切雷斯特开了一间新办公室,使用了由IBM罗马尼亚的现代计算机办公环境,运用了现代信息技术.一般来说,银行的每个顾客都有一个识别码K,并且每一个来银行的顾客都会被给予一个优先级P.银行主管的一个大胆想法震惊了公…

基础表单笔记

表单数据要向服务端提交的话 每个表单都要指定一些属性就是name""和value"" value就是用户写什么就是什么 来提交name就是对这个表单进行一个标识 <from> 输入用户名<input type"text" name"user" value""/>这…

PCIE总线-PCI、PCIE关系及信号定义

PCI(Peripheral Component Interconnect)总线规范在上世纪九十年代由Intel提出。在处理器体系结构中&#xff0c;PCI总线属于局部总线(Local Bus)。局部总线作为系统总线的延伸&#xff0c;主要功能是为了连接外部设备。 处理器主频的不断提升&#xff0c;要求速度更快&#x…

SQL Server:SQL Like 通配符特殊用法:Escape

%&#xff1a;匹配零个及多个任意字符&#xff1b; _&#xff1a;与任意单字符匹配&#xff1b; []&#xff1a;匹配一个范围&#xff1b; [^]&#xff1a;排除一个范围 &#xff1b;-&#xff1a;连字符 Symbol Meaning like 5[%] 5% like [_]n _n like [a-cdf] a, b, c, d, o…

案例篇-HBase RowKey 设计指南

1.为什么 Rowkey 这么重要 1.1 RowKey 到底是什么 我们常说看一张 HBase 表设计的好不好&#xff0c;就看它的 RowKey 设计的好不好。可见 RowKey 在 HBase 中的地位。那么 RowKey 到底是什么?RowKey 的特点 如下: 类似于 MySQL、Oracle 中的主键&#xff0c;用于标示唯一的行…

PCIe简介及引脚定义

随着现代处理器技术的发展&#xff0c;在互连领域中&#xff0c;使用高速差分总线替代并行总线是大势所趋。与单端并行信号相比&#xff0c;高速差分信号可以使用更高的时钟频率&#xff0c;从而使用更少的信号线&#xff0c;完成之前需要许多单端并行数据信号才能达到的总线带…

IDEA下搜狗输入法输入中文时卡着不动的参考解决方法

【问题描述】 在IntelliJ IDEA工具的java编辑窗口&#xff0c;给代码增加注释时发现&#xff0c;输入中文时&#xff0c;搜狗输入法界面不动&#xff0c;只显示第一个字母。如图&#xff1a; 我想输入“根据”两个字&#xff0c;但搜狗输入法界面一直卡着不刷新&#xff0c;导…

6U VPX板卡资料:6U VPX 高性能计算存储板卡

6U VPX板卡资料&#xff1a;6U VPX 高性能计算存储板卡_hexiaoyan827的博客-CSDN博客_vpx板卡

Android: Custom View和include标签的区别

Custom View&#xff0c; 使用的时候是这样的&#xff1a; <com.example.home.alltest.view.MyCustomViewandroid:id"id/customView"android:layout_width"match_parent"android:layout_height"wrap_content"></com.example.home.allte…

七 web爬虫讲解2—urllib库爬虫—状态吗—异常处理—浏览器伪装技术、设置用户代理...

如果爬虫没有异常处理&#xff0c;那么爬行中一旦出现错误&#xff0c;程序将崩溃停止工作&#xff0c;有异常处理即使出现错误也能继续执行下去 1.常见状态吗 301&#xff1a;重定向到新的URL&#xff0c;永久性302&#xff1a;重定向到临时URL&#xff0c;非永久性304&#x…

DVI和HDMI中的TMDS接口协议

TMDS&#xff08;Transition Minimized Differential signal&#xff09;&#xff0c;即过渡调制差分信号&#xff0c;也被称为最小化传输差分信号&#xff0c;是指通过异或及异或非等逻辑算法将原始信号数据转换成10位&#xff0c;前8为数据由原始信号经运算后获得&#xff0c…

君子眼中皆好人

从前有个国王&#xff0c;在晚年时思量 着&#xff1a;“我有两个儿子&#xff0c;我应该把王位传给哪个儿子来统治这个国家呢&#xff1f;”国王决定考验一下他的两位王子&#xff0c;哪位最是忠义仁厚&#xff0c;爱护老百姓的明君。国王叫来长子&#xff0c; 对他说&#xf…

GS使用HTTPS登录的设置过程

1. Windows 增加角色服务 服务器配置管理器&#xff0c; 添加角色服务 增加角色功能里面有&#xff1a; 证书颁发机构 证书颁发机构 web注册 2. AD CS配置 主要是next操作 独立ca 根证书 等 3. inetmgr申请证书 在机器名的一层及上面申请证书 保存证书信息 用来使用CA机构进行签…