程序员的视角:java GC
GC(Garbage Collection 垃圾回收)的概念随着 java 的流行而被人们所熟知。 实际 GC 最早起源于20世纪60年代的 LISP 语言,是一种自动的内存管理机制。 GC 要解决的问题有 3 个:
回收什么?(what)
何时回收?(when)
- 如何回收?(how)
回收什么?
清理的是垃圾,回收的是内存空间。
既然 GC 是 java 的自动内存管理机制,那么先看下 java 虚拟机将所管理的内存划分为不同的区域,如图1。
如图1所示,java 虚拟机管理的内存区域分为如下几个部分:
堆(Heap)
方法区(Method Area)
虚拟机栈(VM Stack)
本地方法栈(Native Method Stack)
- 程序计数器(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。
程序员的视角:java GC
如何回收?
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%。
程序员的视角:java GC
CMS 收集器的处理整个过程有如下步骤:
初始标记:找到 GC Roots。
并发标记:标记所有从 GC Roots 可达的对象。
并发预清理:检查对象引用更新和在并发标记阶段晋升到老年代的对象并进行标记。
重新标记:标记预清理阶段更新的对象引用。
并发清理:回收死亡对象的内存。
- 并发重置:重置数据结构为下次运行作准备。
其执行示意如图3所示
其中步骤1(初始标记)和步骤4( 重新标记)仍然需要 Stop The World,只是相对来说时间较短。
低停顿是 CMS 收集器是的优点,但它也并不完美,它有 3 个明显缺点:
由于和用户线程并发执行,所以存在 CPU 争抢的问题。
无法回收浮动垃圾。
- CMS 仅进行了标记、清除而未进行整理,容易产生大量内存空间碎片。
CMS 默认启动的回收线程是 (CPU数量 + 3) / 4,也就是 CPU 在 4 个以上时并发回收线程使用的 CPU 资源不少于 25%。 在并发清理时新产生的垃圾称为浮动垃圾(Floating Garbage),本次无法收集,当浮动垃圾过多导致预留的内存无法满足程序需要时触发, 就可能出现 Concurrent Mode Failure 导致启用 Serial Old 收集器作为后备进行 Full GC。
Garbage First (G1) Collector
一种新的收集器,在 jdk7u4 开始正式支持,它有如下特点:
- 多分区的堆组织方式
G1 也是分代收集器,但其组织堆的方式与其他收集器完全不同。它根据不同的用途将堆分为大量(~2000)固定大小的区域(region)。 相同用途的堆也并不连续,G1 依然保留了新生代和老年代的概念,但新生代和老年代不再是物理上隔离的了,它们都是一部分 region 的集合,如图4所示。
如果一个对象大小超过了普通区域大小的50%,那么它会被分配到一个大区域(humongous)里面。
- 优先的收集方式
G1 的收集方式追求低停顿,并且建立可预测的停顿时间模型(在 M 毫秒的时间片段内,GC 的时间不得超过 N 毫秒,N < M)。 G1 通过有计划的避免在整个堆中进行全区域扫描进行垃圾收集,它通过跟踪各个 region 中垃圾的价值大小(回收获得的空间及回收所花费的时间的经验值), 在后台维护一个优先级列表,每次根据允许的收集时间,优先回收价值最大的 region,这也正式 Garbage-First 名称的由来。 而对 region 的收集采用的是 Stop-The-World 的方式,增量的将存活的对象复制到一个空 region 里面,这种方式不会产生内存碎片问题。
最后我们引用《Java Garbage Collection Distilled》 一文中的关于 GC 的折衷权衡点来总结下。
俗话说:“从来没有不劳而获,当我们得到某些事物的时候,通常不得不放弃另外一些事物”。
当谈论垃圾收集的时候,我们主要考虑三个收集器的指标:
吞吐量:花费在 GC 上的时间占整个应用程序工作的比例。
延迟:因为垃圾回收,而引起的响应暂停的时间。
- 内存:我系统使用内存来存储状态,在管理的时候它们常常需要复制和移动。
上述三个指标,吞吐量越大越好,延迟越低越好,内存复制和移动产生的碎片越少越好。 但可惜这三个目标很难同时满足,很多时候我们都是根据应用类型在其中做出权衡取舍。
转载于:https://blog.51cto.com/14084567/2340301
程序员的视角:java GC相关推荐
- java程序启动后就进行了7次younggc_程序员如何优化 Java GC
本文由CrowHawk翻译 Sangmin Lee发表在Cubrid上的"Become a Java GC Expert"系列文章的第三篇<How to Tune Java ...
- 优秀Java程序员应该了解的GC工作原理
一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,因为有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率 ,才能提高整个应 ...
- 一个优秀的Java程序员必须了解的GC机制
一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率,才能提高整个应用程序 ...
- java 哪一个gc好_优秀的Java程序员必须了解的GC哪些
作者丨灵犀一脚C http://www.cnblogs.com/ckwblogs/p/5975921.html 一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的 ...
- 转载:一位资深程序员大牛给予Java初学者的学习路线建议
一位资深程序员大牛给予Java初学者的学习路线建议 java学习这一部分其实也算是今天的重点,这一部分用来回答很多群里的朋友所问过的问题,那就是我你是如何学习Java的,能不能给点建议?今天我是打算来 ...
- 什么C++程序员,什么Java程序员
还是那句话,程序员就是程序员,写得出程序就是程序员.程序员不要在自己脑门上加太多修饰,什么C++程序员,什么Java程序员,什么Windows程序员,什么Linux程序员,这些加了修饰的程序员,其实不 ...
- java ide 的优劣_Java程序员的困惑 Java IDE到底怎么选
原标题:Java程序员的困惑 Java IDE到底怎么选 Eclipse和IntelliJIDEA可以说是程序员们最常用的两个IDE开发工具了.IntelliJIDEA被认为是目前Java开发效率最快 ...
- java面向对象程序练习_5本面向经验丰富的程序员的高级Java书籍
java面向对象程序练习 Sometimes back I wrote an article for Best Core Java Books for beginners, today I am sh ...
- (转)一位资深程序员大牛给予Java初学者的学习路线建议
一位资深程序员大牛给予Java初学者的学习路线建议 java学习这一部分其实也算是今天的重点,这一部分用来回答很多群里的朋友所问过的问题,那就是我你是如何学习Java的,能不能给点建议?今天我是打算来 ...
最新文章
- python气象数据可视化_python grib气象数据可视化
- linux创建mysql视图_MySQL视图基本操作
- Leaflet中使用Leaflet.Graticule插件实现添加矩形格网
- 一场惊心动魄的国际黑客入侵保卫战
- 在mysql-workbench的存储过程中使用循环while,repeat,loop
- 若川的2016年度总结,毕业工作
- 属性面板 脚本_如何在组态王中实现同类型设备公用操作面板的调用---干货
- 一起谈.NET技术,C#类类型
- WordPress的wp-cumulus插件------------标签云插件
- Sqlserver2012卸载
- Go语言核心之美 2.4-布尔值
- RK3128-android5.1-wifi兼容
- win10重装系统修改信息
- c语言语法要素,第6章DSP_C语言程序设计要素.ppt
- Python Web前端实战案例——电商网站商品菜单导航栏
- WIN10 LTSC 安装EDGE和微软应用商店
- Sox语音转换的相关知识
- python获取网站代码_python爬虫1——获取网站源代码(豆瓣图书top250信息)
- 大学生生涯人物访谈(程序员篇)
- 你是一名【合格】前端工程师吗
热门文章
- 自动驾驶汽车对保险业的影响
- 基础通俗讲解集成学习算法
- 「每周CV论文推荐」 初学深度学习人脸属性分析必读的文章
- 全球32家人工智能独角兽公司
- 深度学习概述:从感知机到深度网络(找到一篇大牛的文章快围观)
- 深度丨AI 与区块链:两大热门技术,会碰撞出什么样的火花?
- Tensorflow— tensorboard网络运行
- 牛顿第三定律不再起作用:芝加哥大学找到了一种新系统,作用力不等于反作用力...
- 未来,中国空间站将成为怎样的“太空科研站”?
- 口罩巨头挑战“量子霸权”,3个月造出的『最强量子计算机』靠谱不?