一 如何判断对象是垃圾对象

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虚拟机专题之垃圾回收(读书笔记)相关推荐

  1. Java虚拟机专题之内存分配(读书笔记)

    一 虚拟机内存分配策略 1.1 对象优先在Eden区域分配 1.2 大对象直接进入老年代 1.3 长期存活的对象进入老年代 1.4 空间分配担保 1.5 动态对象年龄判定 二 对象优先在Eden区域分 ...

  2. Java虚拟机专题之class文件结构(读书笔记)

    我们知道一个Class文件对应着一个接口或者注解的类,但是他们并不一定定义在文件里,也可以直接由类加载器生成. Java虚拟机定义了专门的数据类型来表示class文件的内容,他们包括u1,u2,u4表 ...

  3. 《深入理解Java虚拟机》阅读——垃圾回收机制

    <深入理解Java虚拟机>阅读--垃圾回收机制 前言 why--为什么需要垃圾回收 what--垃圾回收做些什么 where--去哪里回收垃圾 how--垃圾回收是怎么做的 垃圾是否要回收 ...

  4. 【Java 虚拟机原理】垃圾回收算法 ( 设置 JVM 命令参数输出 GC 日志 | GC 日志输出示例 | GC 日志分析 )

    文章目录 一.设置 JVM 命令参数输出 GC 日志 二.GC 日志示例 三.GC 日志分析 一.设置 JVM 命令参数输出 GC 日志 在 IntelliJ IDEA 的启动参数中设置 -XX:+P ...

  5. 【Java 虚拟机原理】垃圾回收算法 ( Java 虚拟机内存分区 | 垃圾回收机制 | 引用计数器算法 | 引用计数循环引用弊端 )

    文章目录 一.Java 虚拟机内存分区 二.垃圾回收机制 三.引用计数器算法 ( 无法解决循环引用问题 ) 一.Java 虚拟机内存分区 Java 虚拟机内存分区 : 所有线程共有的内存区域 : 堆 ...

  6. 浅谈Java虚拟机JVM的垃圾回收机制

    1. 什么是垃圾 要回收垃圾,那么垃圾是什么?简单的逻辑就是不会再被使用的内存对象呗. 2. 怎么判断不再被使用 2.1 引用计数法.统计有多少个引用指向内存对象,如果没有引用指向内存对象,那么该内存 ...

  7. 深入理解java虚拟机(六)GC垃圾回收-低延迟垃圾收集器(Shenandoah、ZGC)

    文章目录 前言 一.Shenandoah收集器 1.Shenandoah介绍 2.Shenandoah与G1对比 3.Shenandoah工作原理 4.Shenandoah并行整理的核心概念-Broo ...

  8. 【一】深入理解Java虚拟机の内存与垃圾回收

    [深入理解java虚拟机](https://www.zybuluo.com/Yano/note/321063) 目录 1.走进Java 2.Java内存区域 2.1 对象创建过程: 2.2 对象的内存 ...

  9. java虚拟机多久触发垃圾回收_每日一问:讲讲 Java 虚拟机的垃圾回收

    昨天我们用比较精简的文字讲了 Java 虚拟机结构,没看过的可以直接从这里查看: 每日一问:你了解 Java 虚拟机结构么? 今天我们必须来看看 Java 虚拟机的垃圾回收算法是怎样的.不过在开始之前 ...

最新文章

  1. 使用Java合并图片、修改DPI
  2. android studio 一直在 svn performing vcs refresh
  3. js 数据类型_js中检测数据类型的方法汇总
  4. Android studio安装
  5. 7-3 符号三角形 (10 分)(思路+详解)
  6. Mysql 中 delete 与 left join 的问题
  7. PHP获取访问用户IP
  8. android广播代码汇总二__有序广播
  9. Android HorizontalScrollView布局
  10. Choosing Capital for Treeland codeforce 219-D
  11. c++ 结构体和类的区别
  12. 浏览器阻挡cookies_浏览器需要阻止第三方cookie吗?
  13. 51地图API接口的初次使用
  14. c#堆栈跟踪;c#异常原因查找打印;c#打印错误日志
  15. git diff:Linux使用meld做git的diff工具
  16. PANTONE 色号
  17. linux计划任务1
  18. 常量与变量有哪些区别
  19. ESP8266 WIFI 模块串口调试过程-实现通过互联网实现数据远程传输(结尾含驱动代码链接)
  20. 计算机监控盘柜采用什么接地,dcs机柜是什么 dcs机柜接地规范

热门文章

  1. matlab实现谱聚类法图像分割代码,一种基于谱聚类的图像分割方法与系统与流程...
  2. java 静态导入_Java中静态导入的使用
  3. python垃圾回收 循环引用_在做 Python 循环引用垃圾回收实验中的一个小问题, Python3 的 print 是线程安全的吗?...
  4. c语言非法字符空格,98行的四则计算器.(支持括号)加入了非法字符的检测
  5. Java NIO学习篇之PosixFilePermission详解
  6. Java并发编程之线程安全性分析之原子性、可见性、有序性
  7. PostgreSQL11.7逻辑复制压测
  8. python获取服务器文件svn版本信息_如何编程获取SVN版本号?
  9. 2018年计算机二级MySQL真题_2018年3月计算机二级考试MySQL真题及答案2
  10. oracle 数字格式取整,Oracle SQL语句操作数字:取整、四舍五入及格式化