在上一章中,我们了解到程序计数器、虚拟机栈、本地方法栈等线程私有的区域的生命周期都是跟随线程的生命周期的变化而变化的,而java堆和方法区等线程共享的区域的生命周期我们就没有介绍了。
因此,本章的主要内容就是介绍java堆和方法区等线程共享的区域上管理,即java堆和方法区上GC。我们将从以下几个方面进行展开:
要特别注意,本章节主要讨论的是java堆和方法区上的GC。
要想了解GC,首先要了解什么是垃圾对象,或者说是什么样的对象才符合垃圾回收的原则?所谓的垃圾就是在jvm中用不到的内存区域,因为内存资源总是有限的,用不到的内存一定要重新被操作系统收集整理后再次使用。所以,垃圾回收的本质就是对内存空间的重新整理,把符合特定条件的内存区域重新回收,以供操作系统后期再次使用。
而线程私有的内存空间由线程自己管理,那线程共享的区域的管理就需要额外的功能模块来管理了,这个额外的功能模块就是垃圾回收器,又因为线程共享的区域上存储的主要内容是java的类对象和方法。因此我们就要研究什么样的对象才能够被回收。
研究对象是否被回收,只需要研究对象是否被使用即可。换句话来说,就是研究对象是否被引用。目前来说,判断对象是否被引用有两种方式:
引用计数法:为每一个对象都创建一个引用计数的属性,如果有其他对象引用这个对象,就为这个对象的引用计数的属性加一,引用释放时计数就减一,计数为0的时候就可以被回收了。这种方式无法解决对象之间相互引用的问题。当然java中也不会使用这种方式来判断对象是否存活了。
可达性分析法:引入GCroot概念,如果在GCroot和一个对象之间没有可达路径,那么对象就是不可达的。要注意的是:不可达对象不等价于可回收对象,不可达对象变成回收对象至少要经过两次标记过程,两次标记之后仍然是可回收对象,那将面临回收。
在Java语言中,GC Roots包括:
TODO: 可达性分析法的具体实现过程
由于java虚拟机规范中,没有要求虚拟机在方法区实现垃圾收集,因此方法区的垃圾收集是根据具体的虚拟机实现来确定的。在HotSpot中,方法区就是永久区。永久区的垃圾回收对象是废弃常量和无用的类。
判断一个类是否无用比较苛刻,要同时满足以下3个方面的条件:
垃圾回收由两部分组成,一部分是GC算法,另一部分是实现GC算法的回收器。
本小节主要研究垃圾回收算法的种类、具体实现原理、适用对象、优缺点等内容。
标记-整理算法
复制算法
标记-清除算法
分代回收算法
在早起的jdk版本中,我们关注的是清理jvm中所有的垃圾,因此产生了单线程的Serial收集器和Serial Old收集器,这保证了清理时的稳定性与高效性,但这样同样会带来一下问题,如由于清理线程独占系统资源造成用户程序线程暂停而引起全局停顿的问题;后来,我们开始关注系统的吞吐量(即用户应用程序运行时间与总运行时间的比值,吞吐量从某种意义上也体现了全局停顿时间的概念),我们认为要提高系统的吞吐量,因此产生了Parallel Scavenge收集器和Parallel Old收集器;再后来,我们把关注点转移到减少全局停顿时间上,因此产生了跨时代的CMS收集器。
此外,由于在垃圾收集过程中,会产生全局停顿的现象,这就引出我们评判一个垃圾收集器性能好坏的评判标准:我们在评判一个垃圾收集器性能好坏时,本质上讲,我们是在讨论这个垃圾收集器在收集垃圾时所产生的全局停顿时间的长短,即 全局停顿时间越短,垃圾收集器的性能越好
。但是这句话不是绝对的,我们只是提供了一种观察垃圾收集器性能好坏的方法。
下面的内容,我们是从分代的角度去讲述垃圾收集器的种类,当然读者也可以从垃圾收集器的发展阶段中的关注点来划分。本小节中也将主要讲解各种回收器的种类、适用范围、实现原理、优缺点、参数控制等内容。
上面的图非常重要! 说明: 如果两个收集器之间存在连线说明他们之间可以搭配使用。
Serial收集器
-XX:+UseSerialGC
参数控制 ParNew收集器
-XX:ParallelGCThreads
Parallel Scavenge收集器
-XX:MaxGCPauseMillis
参数来控制最大垃圾收集停顿时间 -XX:GCTimeRatio
参数来直接设置吞吐量大小 -XX:+UseAdaptiveSizePolicy
参数来开启 GC自适应的调节策略
,这个也是与Par New收集器的重要区别 三个新生代收集器所采用的收集算法都是复制算法,并且它们的功能也是逐步完善的。ParNew收集器在Serial收集器的基础上面加上了多线程的功能,Parallel Scavenge收集器又在ParNew收集器的基础上加了控制吞吐量的控制功能。
Serial Old收集器
Parallel Old收集器
:star: CMS收集器
-XX:+CMSFullGCsBeforeCompaction
参数设置执行多少次不压缩FullGC后,跟着来一次带压缩的。如:这个参数的值为2,就是在执行2次不压缩的FullGC之后,紧接着会再执行1次压缩的FullGC)等 G1收集器(又称:Garbage—First)是目前技术发展的最前沿成果之一,它既可以作用到新生代又可以作用到老年代,因此它也被称为通用收集器。
相对于CMS垃圾收集器,它具有以下特点:
使用范围
G1收集器的几个重要内容
G1堆分配
-XX:G1HeapRegionSize
参数(该参数的默认值并不是一定的,它是根据java堆初始化时大小决定的,取值范围为:1M到32M,且要是2的指数)来设置。 新生代GC
老年代GC
GC的停顿主要来源于可达性分析上,程序执行时并非在所有地方都能停顿下来开始GC,只有在到达安全点时才能暂停。
安全点的选定基本上是以程序“是否具有让程序长时间执行的特征”为标准进行选定的——因为每条指令执行的时间都非常短暂,程序不太可能因为指令流长度太长这个原因而过长时间运行,“长时间执行”的最明显特征就是指令序列复用,例如方法调用、循环跳转、异常跳转等,所以具有这些功能的指令才会产生安全点。
接下来的问题就在于,如何让程序在需要GC时都跑到安全点上停顿下来,大多数JVM的实现都是采用主动式中断的思想。
主动式中断的思想是当GC需要中断线程的时候,不直接对线程操作,仅仅简单地设置一个标志,各个线程执行时主动去轮询这个标志,发现中断标志为真时就自己中断挂起,轮询标志的地方和安全点是重合的,另外再加上创建对象需要分配内存的地方。
// TODO
Serial收集器和SerialOld收集器
ParNew收集器与SerialOld收集器
Parallel Scavenge收集器和Parallel Old收集器
G1收集器
MinorGC日志格式
FullGC日志格式
调优工具分为两类,一类是jdk自带的,一类是第三方的。
jps:JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。
jstat:JVM statistics Monitoring是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。
jmap:JVM Memory Map命令用于生成heap dump文件
jhat:JVM Heap Analysis Tool命令是与jmap搭配使用,用来分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看
jstack:用于生成java虚拟机当前时刻的线程快照。
jinfo:JVM Configuration info 这个命令作用是实时查看和调整虚拟机运行参数。
jconsole:Java Monitoring and Management Console是从java5开始,在JDK中自带的java监控和管理控制台,用于对JVM中内存,线程和类等的监控
jvisualvm:jdk自带全能工具,可以分析内存快照、线程快照;监控内存变化、GC变化等。
MAT,Memory Analyzer Tool,一个基于Eclipse的内存分析工具,是一个快速、功能丰富的Java heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗
GChisto:一款专业分析gc日志的工具