Java虚拟机专题之垃圾回收(读书笔记)
一 如何判断对象是垃圾对象
1.1 引用计数法 (Reference Counting)
在对象中添加一个引用计数器,当有其他地方引用这个对象的时候,引用计数器就加1,当引用失效的时候就-1. 当垃圾回收器检查到引用为0,就会认为是垃圾对象,进行回收。
但是有一个问题,比如对象之间循环引用,诸如A,B两个对象,都有一个属性instance, 假设A.instance = B,B.instance=A。他们互相引用着对方,导致对方引用计数器都不为0。
所以JVM一般都不使用这种方法,因为难以解决对象之间相互循环引用问题。
1.2 可达性分析法 (ReachabilityAnalysis)
通过一系列GC Roots对象作为起点,这些节点开始向下搜索,搜索走过的路径叫做引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连接,则证明此对象是不可用的。
对于可达性分析,我们知道GC Roots是很重要的,那么有哪些对象可以作为GC Roots呢?
# 虚拟机栈中引用的对象
# 方法区中静态类属性引用的对象
# 方法区中常量池引用的对象
# 本地方法栈中JNI引用的对象
如下面2图所示:
第一幅图中,除了object4 没有到任何GC Roots的引用链相连接,其余的都有,所以可以认为object4 是不可用的
第二幅图中因为object1到GC Roots的引用链没了,所以object1下面的引用链上对象都不可用,因为都没引用链连接到对应的GC Roots
二 垃圾回收算法(回收策略)
2.1 标记-清除算法(Mark-Sweep)
标记出需要回收的对象,如何标记呢,就是通过可达性分析法,然后有一个清除线程来将标记的对象清除掉。
# 效率问题:标记清除效率都不高
# 空间问题: 内存碎片问题
标记清除之后,会产生大量不连续的没存碎片,空间碎片太多可能会导致以后程序要分配较大的对象时,无法找到足够的连续内存,而不得不提前触发一次新的垃圾收集动作。
2.2 复制算法(Copying)
虚拟机将内存容量划分为大小相等的2块,每一次只使用其中一块。当其中一块用完了,就将存活的对象复制到另一块上去,然后已使用这块内存清理。
优点:
效率高,解决了内存碎片问题
缺点:
浪费内存;对象存活率较高的时候,会进行较多的复制操作,效率将变得低下
所以其特点也决定,只适合在新生代区域使用,不适合老年代,因为老年代的对象生命周期都比较长,所以如果出现极端情况,100%的存活率,就容易发生Full GC
2.3 标记-整理算法(Mark-Compact)
我们知道,老年代不适合使用复制算法,所以根据老年代的特点,提出了标记整理算法。
# 先进行标记
# 标记完了之后,不是直接进行清理,而是让所有存活的对象都往一段移动
# 移动完了之后,直接从边界处清理掉不可用对象
优点
解决了标记-清除算法中的碎片问题;也解决了复制算法中内存浪费问题
缺点:效率不高
所以不适合频繁进行GC的场景,而老年代GC的发生次数比较少,所以适合老年代
2.4 分代收集算法(GenerationalCollection)
根据对象对象不同的存活周期,将堆内存划分成新生代和老年代。
针对不同的年龄代使用不同的垃圾收集算法。
比如新生代,经常有大量对象死去,只有少量存活可以使用复制算法,而老年代因为存活率较高,所以一般使用标记清理或者标记整理算法。
三 垃圾收集器
3.1 Serial收集器
JDK1.3 之前使用的垃圾收集器,它是单线程的。但是这个单线程必须暂停所有的工作线程,然后再进行垃圾回收,直到它收集结束。
很明显,这种垃圾回收方式是令人难以接受的。
开启方式:-XX:+UseSerialGC
3.2 ParNew收集器
ParNew除了是多线程进行垃圾回收以外,其余的特点和Serial几乎是一样的。可以认为是Serial的多线程版本。
Serial 和 ParNew比较
相同点:
# 暂停所有工作线程,然后进行垃圾回收
# 采用复制算法,对新生代进行垃圾回收
不同点:
# Serial是单线程;ParNew是多线程
开启方式:-XX:+UseParNewGC
3.3 ParallelScavenge 收集器
Parallel也是一个新生代采用复制算法的收集器,而且也是多线程收集器,感觉和ParNew差不多。
ParNew 和 Parallel 收集器比较
相同点:
# 采用复制算法,对新生代进行垃圾回收
# 使用多线程进行垃圾回收
不同点:
关注点不一样,CMS,ParNew等收集器关注的是尽可能缩短垃圾收集时用户线程的停顿时间;而Parallel 收集器目标是达到一个可控制的吞吐量。
什么是吞吐量?
吞吐量 = (运行用户代码时间) / (运行用户代码时间 + 垃圾收集时间)
如果虚拟机总共运行了100分钟,而垃圾收集花了1分钟,那么吞吐量就是99%。
Parallel提供了2个参数用于精确控制吞吐量
-XX:MaxGCPauseMills: 垃圾收集器最大停顿时间(毫秒)
-XX:GCTimeRatio: 吞吐量大小(值应该大于0且小于100)
开启Parallel和Parallel Old收集器的方式:
-XX:+UseParallelGC -XX:+UseParallelOldGC
3.4CMS收集器
CMS是Concurrent Mark Sweep,即并发标记删除,而不是内容管理系统的缩写。在这个过程中,部分阶段所有线程暂停,部分阶段会和应用线程一起并发执行
工作过程
# 初始标记 (CMS initial mark)
会暂停整个虚拟机,然后标记所有的跟对象(GCRoots),但是GC Roots一般都很少,所以这个过程很快。
# 并发标记 (CMS concurrent mark )
虚拟机会从根节点开始,将所有引用到的对象都打上标记,而且这个阶段是和应用线程一起并发执行
# 重新标记 (CMS remark)
会暂停整个虚拟机。由于之前是并发执行,那么一边标记,一边可能会有一些新的更新,比如之前是有引用的对象,现在没有引用了,所以需要重新标记
# 并发清除 (CMS concurrent sweep)
将没有标记的对象作为垃圾回收掉,这个阶段也是和应用线程一起工作的。
优点
并发收集,低停顿
缺点
# 占用的CPU资源更多
# 无法处理浮动垃圾(Floating Garbage),可能出现Concurrent Model Failure
# 由于是基于标记清除算法,所以存在内存碎片问题
为什么存在无法处理浮动垃圾(FloatingGarbage),可能出现ConcurrentModel Failure而导致另一次的Full GC产生的原因分析?
浮动垃圾:CMS并发清理阶段,用户线程还在继续运行,就有垃圾不断产生,由于出现在了重新标记之后,CMS无法再次收集处理掉,只能留到下一次GC时再来清理。这就是浮动垃圾。
垃圾收集阶段,用户线程还要继续运行,那也就需要预留有足够的内存 空间给用户线程使用,因此CMS不像其他的收集器,等待老年代都填满了才再进行收集。
如果老年代不是增长的很快,那么可以适当提高参数:
-XX:CMSInitiatingOccupancyFractiont,以提升触发的百分比
JDK1.6 中,CMS收集器启动阀值时92%,要是CMS运行期间预留的内存无法满足程序需要就会出现Concurrent Model Failure失败,这时候虚拟机启动预备方案:临时启用Serial Old对老年代进行收集,这样停顿时间就长了。
所以说参数-XX:CMSInitiatingOccupancyFraction设置太高容易导致Concurrent Model Failure失败,性能反而降低了。
Java虚拟机专题之垃圾回收(读书笔记)相关推荐
- Java虚拟机专题之内存分配(读书笔记)
一 虚拟机内存分配策略 1.1 对象优先在Eden区域分配 1.2 大对象直接进入老年代 1.3 长期存活的对象进入老年代 1.4 空间分配担保 1.5 动态对象年龄判定 二 对象优先在Eden区域分 ...
- Java虚拟机专题之class文件结构(读书笔记)
我们知道一个Class文件对应着一个接口或者注解的类,但是他们并不一定定义在文件里,也可以直接由类加载器生成. Java虚拟机定义了专门的数据类型来表示class文件的内容,他们包括u1,u2,u4表 ...
- 《深入理解Java虚拟机》阅读——垃圾回收机制
<深入理解Java虚拟机>阅读--垃圾回收机制 前言 why--为什么需要垃圾回收 what--垃圾回收做些什么 where--去哪里回收垃圾 how--垃圾回收是怎么做的 垃圾是否要回收 ...
- 【Java 虚拟机原理】垃圾回收算法 ( 设置 JVM 命令参数输出 GC 日志 | GC 日志输出示例 | GC 日志分析 )
文章目录 一.设置 JVM 命令参数输出 GC 日志 二.GC 日志示例 三.GC 日志分析 一.设置 JVM 命令参数输出 GC 日志 在 IntelliJ IDEA 的启动参数中设置 -XX:+P ...
- 【Java 虚拟机原理】垃圾回收算法 ( Java 虚拟机内存分区 | 垃圾回收机制 | 引用计数器算法 | 引用计数循环引用弊端 )
文章目录 一.Java 虚拟机内存分区 二.垃圾回收机制 三.引用计数器算法 ( 无法解决循环引用问题 ) 一.Java 虚拟机内存分区 Java 虚拟机内存分区 : 所有线程共有的内存区域 : 堆 ...
- 浅谈Java虚拟机JVM的垃圾回收机制
1. 什么是垃圾 要回收垃圾,那么垃圾是什么?简单的逻辑就是不会再被使用的内存对象呗. 2. 怎么判断不再被使用 2.1 引用计数法.统计有多少个引用指向内存对象,如果没有引用指向内存对象,那么该内存 ...
- 深入理解java虚拟机(六)GC垃圾回收-低延迟垃圾收集器(Shenandoah、ZGC)
文章目录 前言 一.Shenandoah收集器 1.Shenandoah介绍 2.Shenandoah与G1对比 3.Shenandoah工作原理 4.Shenandoah并行整理的核心概念-Broo ...
- 【一】深入理解Java虚拟机の内存与垃圾回收
[深入理解java虚拟机](https://www.zybuluo.com/Yano/note/321063) 目录 1.走进Java 2.Java内存区域 2.1 对象创建过程: 2.2 对象的内存 ...
- java虚拟机多久触发垃圾回收_每日一问:讲讲 Java 虚拟机的垃圾回收
昨天我们用比较精简的文字讲了 Java 虚拟机结构,没看过的可以直接从这里查看: 每日一问:你了解 Java 虚拟机结构么? 今天我们必须来看看 Java 虚拟机的垃圾回收算法是怎样的.不过在开始之前 ...
最新文章
- 使用Java合并图片、修改DPI
- android studio 一直在 svn performing vcs refresh
- js 数据类型_js中检测数据类型的方法汇总
- Android studio安装
- 7-3 符号三角形 (10 分)(思路+详解)
- Mysql 中 delete 与 left join 的问题
- PHP获取访问用户IP
- android广播代码汇总二__有序广播
- Android HorizontalScrollView布局
- Choosing Capital for Treeland codeforce 219-D
- c++ 结构体和类的区别
- 浏览器阻挡cookies_浏览器需要阻止第三方cookie吗?
- 51地图API接口的初次使用
- c#堆栈跟踪;c#异常原因查找打印;c#打印错误日志
- git diff:Linux使用meld做git的diff工具
- PANTONE 色号
- linux计划任务1
- 常量与变量有哪些区别
- ESP8266 WIFI 模块串口调试过程-实现通过互联网实现数据远程传输(结尾含驱动代码链接)
- 计算机监控盘柜采用什么接地,dcs机柜是什么 dcs机柜接地规范
热门文章
- matlab实现谱聚类法图像分割代码,一种基于谱聚类的图像分割方法与系统与流程...
- java 静态导入_Java中静态导入的使用
- python垃圾回收 循环引用_在做 Python 循环引用垃圾回收实验中的一个小问题, Python3 的 print 是线程安全的吗?...
- c语言非法字符空格,98行的四则计算器.(支持括号)加入了非法字符的检测
- Java NIO学习篇之PosixFilePermission详解
- Java并发编程之线程安全性分析之原子性、可见性、有序性
- PostgreSQL11.7逻辑复制压测
- python获取服务器文件svn版本信息_如何编程获取SVN版本号?
- 2018年计算机二级MySQL真题_2018年3月计算机二级考试MySQL真题及答案2
- oracle 数字格式取整,Oracle SQL语句操作数字:取整、四舍五入及格式化