JVM学习笔记三:垃圾收集器及内存管理策略
垃圾收集器
上文说到了垃圾收集算法,这次我们聊一下HotSpot的具体垃圾收集器的实现,以JDK1.7为例,其包含的可选垃圾收集器如下图:

不同收集器之间的连线,代表它们可以搭配使用,收集器所属的区域代表它们属于新生代收集器还是老年代收集器,下面总结一下每个收集器的特点:
Serial 收集器
Serial 字面意思为串行,这与它的工作方式是有关系的,因为它是一个单线程收集器,他在新生代工作,采取的是复制算法,在单CPU的机器上可以高效的完成收集工作,其对于运行在Client模式下的虚拟机是一个很好的选择。
ParNew收集器
ParNew收集器其实就是Serial收集器的多线程版本,除了采用多线程进行垃圾收集之外,其余行为包括控制参数、收集算法、STW、对象分配法则、回收策略都与Serial收集器完全一样。由于采用多线程ParNew通常是虚拟机运行在Server模式下的首选新生代收集器,ParNew收集器能与CMS收集器配合工作,他默认开启的收集线程数与CPU的数目相同,可以使用-XX:ParallelGCThread 参数设施线程数量
Parallel Scavenge收集器
从名称可以看出Parallel Scavenge也是并行的多线程收集器,工作在新生代,采用复制算法进行收集,那和ParNew相比它有什么特别之处呢?与其他收集器的关注点不同,CMS等收集器的关注点是尽可能的缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是吞吐量,所谓吞吐量是指CPU用于运行用户代码的时间与CPU总消耗时间的比值。如果说实际应用场景,那么CMS等停顿时间较短的收集器适合需要与用户进行交互的程序,以获得良好的响应速度,而Parallel Scavenge收集器具有较高的吞吐量则可以高效的利用CPU时间,尽快完成运算任务,适合后台运算不需要太多交互的任务。
Parallel Scavenge收集器提供了两个参数可以用于精确控制吞吐,分别是最大垃圾收集停顿时间:-XX:MaxGCPauseMillis 以及知己设置吞吐量大小的-XX:FCGCTimeRatio参数,另外还提供了一个自动调节的参数:-XX:PretenureSize
Serial Old收集器
它是Serial收集器的老年代版本,同样是一个单线程收集器,使用标记-整理算法
Parallel Old收集器
他是Parallel Scavenge 收集器的老年代版本,使用标记-整理算法,配合PS收集器使用。
CMS收集器 Concurrent Mark Sweep
CMS是一种以获取最短回收暂停时间为目标的收集器,从名称可以看出CMS是基于标记-整理算法实现的,它的运作过程比较复杂,整个过程分为4个步骤:
- 初始标记 initial mark
这个阶段会扫描root对象直接关联的可达对象。注意不会递归的追踪下去,只是到达第一层而已。这个过程,会STW,但是时间很短。 - 并发标记 concurrent mark
这个阶段是进行真正的GC Tracing,递归分析存活对象,无须STW - 重新标记 remark
需要STW,因为并发标记阶段,用户程序继续运行,所以重写标记那些产生变化的对象。 - 并发删除 concurrent sweep
无须STW,对分析出的死亡对象进行清理。
CMS收集器的缺点
- 对CPU资源敏感
由于并行的特性,需要占用cpu资源,影响用户程序性能,CMS默认启动的回收线程是(CPU数量 + 3)/ 4 当CPU数量小于4个时,CPU数量越少垃圾回收线程占比越大。
- 无法处理浮动垃圾
Floating Garbage,可能会出现Concurrent Mode Failure,而导致另一次Full GC的产生,浮动垃圾是什么,由于并发删除阶段用户线程还在执行,自然还会产生新的垃圾,这部分垃圾在标记步骤之后,所以无法清理,只要等待下一次GC。由于并发的特性,CMS的不能像其他收集器那样等到老年代几乎完全被填满再进行收集,需要预留一部分空间提供并发收集时的用户程序使用,JDK1.5的默认设置是老年代使用了68%的空间后就会被激活,1.6为92%,这个比例可以通过参数 -XX:CMSInitiatingOccupancyFraction来调整,如果GC期间预留的内存空间无法满足用户程序要求,就会出现一次“Concurrent Mode Failure”,这时会启动后备预案,临时启用Serial Old收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了。
- 内存碎片
由于CMS采用标记-删除算法,会产生大量的内存碎片,造成分配大对象时,虽然老年代存在不少剩余空间,但找不到可用的内存空间,不得不提前触发一次Full GC,为了解决这个问题,CMS提供了一个-XX:UseCMSCompactAtFullCollection 开关参数,用于在CMS顶不住进行Full GC时开启内存碎片整理合并,由于整理过程无法并发,所以停顿时间不得不边长,另外还有一个参数-XX:CMDSFullGCsBeforeCompaction,这个参数是用于设置执行多少次不压缩的Full GC后,再进行一次带压缩的Full GC,默认为0,表示每次都进行碎片整理。
G1 收集器
Garbage First 是最前沿的收集器之一,在JDK1.7 u4开始商用,它具有如下特点:
1.并发与并行 充分利用多CPU和多核的硬件优势,来缩短STW停顿的时间
2.分代收集,且不需要其他收集器配合就可以独立管理整个Java堆
3.整体上采用标记-整理算法,内部不同Region之间采用复制算法。
4.可预测的停顿,使用者可以指定消耗在垃圾回收上的时间。
比较特别的是G1收集器将整个Java对划分为多个大小相等的独立区域Region,虽然仍然保留新生代和老年代,但已经不再有明显的物理界限,他们只是一部分Region的集合。G1会跟踪各个Region里的垃圾堆积价值大小(回收所获得的空间大小以及所需时间的经验值),维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region。
内存分配与回收策略
- 对象优先在Eden分配
大多数情况下,对象在新生代Eden区中分配,当Eden没有足够空间时,将发生一次MinorGC,虚拟机提供了-XX:PrintGCDetails这个参数,在虚拟机发生垃圾收集时打印内存回收日志,并在内存退出时输出当前的内存各区域分配情况。(MinorGC又称新生代GC,MajorGC/FullGC又称老年代C)。
- 大对象直接进入老年代
java虚拟机提供了一个-XX:PretenureSizeThreshold参数,设置直接在老年代分配的对象的大小,这样可以避免在Eden和两个Survivor区域之间发生大量内存复制。(这个参数只对Serial和ParNew收集器有效)
- 长期存活对象将进入老年代
虚拟机为每个对象定义了一个年龄(Age)计数器,如果对象在Eden区域出生并经过第一次MinorGC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并且对象年龄设为1,对象在Survivor区域熬过一次MinorGC,年龄就加1,当年龄增加到一定程度(默认是15岁),就会被晋升为老年代,这个阀值可以通过参数:-XX:MaxTenuringThreshold设置。
- 动态对象年龄判定
为了更好的适应不同程序的内存情况,虚拟机并不是永远的要求对象的年龄必须达到了MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代。
- 空间分配担保
在发生MinorGC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象的空间,如果这个条件成立,那么MinorGC就是安全的,如果不成立,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败,如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试进行一次MinorGC,尽管这次MinorGC有风险;如果小于,或者HandlePromotionFailure设置不允许冒险,这是也要改为进行一次Full GC。
参考资料
本文参考:《深入理解Java虚拟机》
JVM学习笔记三:垃圾收集器及内存管理策略的更多相关文章
- 《深入java虚拟机》读书笔记之垃圾收集器与内存分配策略
前言 该读书笔记用于记录在学习<深入理解Java虚拟机--JVM高级特性与最佳实践>一书中的一些重要知识点,对其中的部分内容进行归纳,或者是对其中不明白的地方做一些注释.主要是方便之后进行 ...
- JVM基础知识2 垃圾收集器与内存分配策略
如何判断堆中的哪些对象可以被回收 主流的程序语言都是使用根搜索算法(GC Roots Tracing)判定对象是否存活 基本思路是:通过一系列名为“GC Roots”的对象作为起点,从这些节点开始向下 ...
- 深入理解JVM(三)垃圾收集器和内存分配策略
3.1 关于垃圾收集和内存分配 垃圾收集和内存分配主要针对的区域是Java虚拟机中的堆和方法区: 3.2 如何判断对象是否“存活”(存活判定算法) 垃圾收集器在回收对象前判断其是否“存活”的两个算法: ...
- JVM学习笔记——GC垃圾收集器
GC 垃圾收集器 Java 堆内存采用分代回收算法,因此 JVM 针对新生代和老年代提供了多种垃圾收集器. 1. Serial 收集器 Serial 收集器是单线程收集器,采用复制算法. 是最基本的垃 ...
- JVM系列2:垃圾收集器与内存分配策略
垃圾收集是一个很大话题,本文也只是看了深入理解Java虚拟机总结了下垃圾收集的知识. 首先按照惯例,先上思维导图: 垃圾收集简而言之就是JVM帮我们清理掉内存区域不需要的数据.它主要负责清理堆中实例对 ...
- 《深入理解 Java 虚拟机》读书笔记:垃圾收集器与内存分配策略
正文 垃圾收集器关注的是 Java 堆和方法区,因为这部分内存的分配和回收是动态的.只有在程序处于运行期间时才能知道会创建哪些对象,也才能知道需要多少内存. 虚拟机栈和本地方法栈则不需要过多考虑回收的 ...
- 《深入理解Java虚拟机》读书笔记:垃圾收集器与内存分配策略
请移步至:http://zhanjindong.info/2014/05/18/java-gc/
- JVM学习笔记-第三章-垃圾收集器与内存分配策略
JVM学习笔记-第三章-垃圾收集器与内存分配策略 tips:对于3.4之前的章节可见博客:https://blog.csdn.net/sanhewuyang/article/details/95380 ...
- java虚拟机学习-JVM内存管理:深入垃圾收集器与内存分配策略(4)
Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述: 说起垃圾收集(Garbage Collection,下文简称GC),大部分人都把这项 ...
随机推荐
- 使用EF连接Postgresql
环境: VS2017 Community Windows 10 Postgresql 9.6 安装Postgresql: https://www.postgresql.org/download/ 1. ...
- MYSQL导入数据报错|MYSQL导入超大文件报错|MYSQL导入大数据库报错:2006 - MySQL server has gone away
导SQL数据库结构+数据时,如果数据是批量插入的话会报错:2006 - MySQL server has gone away. 解决办法:找到你的mysql目录下的my.ini配置文件(如果安装目录没 ...
- 跨域CORS
一.跨域CORS是什么 当一个资源从与该资源本身所在的服务器的域或端口不同的域或不同的端口请求一个资源时,浏览器会发起一个跨域 HTTP 请求.出于安全考虑,浏览器会限制从脚本内发起的跨域HTTP请求 ...
- 学容器必须懂 bridge 网络 - 每天5分钟玩转 Docker 容器技术(32)
上一节我们讨论了 none 和 host 类型的容器网络,本节学习应用最广泛也是默认的 bridge 网络. Docker 安装时会创建一个 命名为 docker0 的 linux bridge.如果 ...
- arcgis api for js入门开发系列十二地图打印(GP服务)
上一篇实现了demo的地图统计图,本篇新增地图打印,截图如下: (1)地图打印实现的思路如下:首先在创建好地图打印GP模型,设置好模型的参数:其次是验证模型运行模型:然后是发布地图打印的GP服务:最后 ...
- 玩转spring boot——websocket
前言 QQ这类即时通讯工具多数是以桌面应用的方式存在.在没有websocket出现之前,如果开发一个网页版的即时通讯应用,则需要定时刷新页面或定时调用ajax请求,这无疑会加大服务器的负载和增加了客户 ...
- loadrunner 手工参数拼接与l oadrunner的url编码
Acction() { //演示需要的一些变量,提前声明 char *name = "yezi_zh"; "; char *work = "engin" ...
- 【LeetCode】237. Delete Node in a Linked List
题目: Write a function to delete a node (except the tail) in a singly linked list, given only access t ...
- 【Android Developers Training】 91. 解决云储存冲突
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- 用py2exe将python文件转换成exe可执行程序
1.首先需要安装py2exe模块,下载地址:http://www.lfd.uci.edu/~gohlke/pythonlibs/ 然后用pip install 命令安装py2exe模块,如果你用的py ...