一.何为垃圾()
1.1 、引用计数算法(Reference Counting)
介绍:给对象添加一个引用计数器,每当一个地方引用它时,数据器加1;当引用失效时,计数器减1;计数器为0的即可被回收。
优点:实现简单,判断效率高
缺点:很难解决对象之间的相互循环引用(objA.instance = objB; objB.instance = objA)的问题,所以java语言并没有选用引用计数法管理内存
1.2 、可达性分析算法
在主流商用语言(如Java、C#)的主流实现中, 都是通过可达性分析算法来判定对象是否存活的: 通过一系列的称为 GC Roots 的对象作为起点, 然后向下搜索; 搜索所走过的路径称为引用链/Reference Chain, 当一个对象到 GC Roots 没有任何引用链相连时, 即该对象不可达, 也就说明此对象是不可用的, 如下图: Object5、6、7 虽然互有关联, 但它们到GC Roots是不可达的, 因此也会被判定为可回收的对象:
- 在Java, 可作为GC Roots的对象包括:
- 方法区: 类静态属性引用的对象;
- 方法区: 常量引用的对象;
- 虚拟机栈(本地变量表)中引用的对象.
- 本地方法栈JNI(Native方法)中引用的对象。
注: 即使在可达性分析算法中不可达的对象, VM也并不是马上对其回收, 因为要真正宣告一个对象死亡, 至少要经历两次标记过程: 第一次是在可达性分析后发现没有与GC Roots相连接的引用链, 第二次是GC对在F-Queue执行队列中的对象进行的小规模标记(对象需要覆盖
finalize()
方法且没被调用过).
二.垃圾收集算法
2.1 垃圾收集器三种类型:串行收集器、并行收集器、并发收集器 。
- 串行收集器
使用单线程处理所有垃圾回收工作,因为无需多线程交互,所以效率比较高。但是,也无法使用多处理器的优势,所以此收集器适合单处理器机器。当然,此收集器也可以用在小数据量(100M 左右)情况下的多处理器机器上。可以使用-XX:+UseSerialGC 打开。- 并行收集器
- 对年轻代进行并行垃圾回收,因此可以减少垃圾回收时间。一般在多线程多处理器机器上使用。使用-XX:+UseParallelGC .打开。并行收集器在J2SE5.0第六6更新上引入,在Java SE6.0中进行了增强--可以堆年老代进行并行收集。如果年老代不使用并发收集的话,是使用单线程进行垃圾回收 ,因此会制约扩展能力。使用-XX:+UseParallelOldGC 打开。
- 使用-XX:ParallelGCThreads=<N> 设置并行垃圾回收的线程数。此值可以设置与机器处理器数量相等 。
- 此收集器可以进行如下配置:
- 最大垃圾回收暂停: 指定垃圾回收时的最长暂停时间,通过-XX:MaxGCPauseMillis=<N> 指定。<N>为毫秒.如果指定了此值的话,堆大小和垃圾回收相关参数会进行调整以达到指定值 。设定此值可能会减少应用的吞吐量。
- 吞吐量: 吞吐量为垃圾回收时间与非垃圾回收时间的比值 ,通过-XX:GCTimeRatio=<N> 来设定,公式为1/(1+N) 。例如,-XX:GCTimeRatio=19时,表示5%的时间用于垃圾回收。默认情况为99,即1%的时间用于垃圾回收。
- 并发收集器
可以保证大部分工作都并发进行(应用不停止),垃圾回收只暂停很少的时间,此收集器适合对响应时间要求比较高的中、大规模应用。使用-XX:+UseConcMarkSweepGC 打开。
- 并发收集器主要减少年老代的暂停时间,他在应用不停止的情况下使用独立的垃圾回收线程,跟踪可达对象。在每个年老代垃圾回收周期 中,在收集初期并发收集器会对整个应用进行简短的暂停,在收集中还会再暂停一次。第二次暂停会比第一次稍长,在此过程中多个线程同时进行垃圾回收工作。
- 并发收集器使用处理器换来短暂的停顿时间 。在一个N个处理器的系统上,并发收集部分使用K/N 个可用处理器进行回收,一般情况下1<=K<=N/4 。
- 在只有一个处理器的主机上使用并发收集器 ,设置为incremental mode 模式也可获得较短的停顿时间。
- 浮动垃圾 :由于在应用运行的同时进行垃圾回收,所以有些垃圾可能在垃圾回收进行完成时产生,这样就造成了“Floating Garbage”,这些垃圾需要在下次垃圾回收周期时才能回收掉。所以,并发收集器一般需要20% 的预留空间用于这些浮动垃圾。
- Concurrent Mode Failure :并发收集器在应用运行时进行收集,所以需要保证堆在垃圾回收的这段时间有足够的空间供程序使用,否则,垃圾回收还未完成,堆空间先满了。这种情况下将会发生“并发模式失败”,此时整个应用将会暂停,进行垃圾回收。
- 启动并发收集器 :因为并发收集在应用运行时进行收集,所以必须保证收集完成之前有足够的内存空间供程序使用,否则会出现“Concurrent Mode Failure”。通过设置-XX:CMSInitiatingOccupancyFraction=<N> 指定还有多少剩余堆时开始执行并发收集
- 小结
- 串行处理器:
--适用情况:数据量比较小(100M左右);单处理器下并且对响应时间无要求的应用。
--缺点:只能用于小型应用- 并行处理器:
--适用情况:“对吞吐量有高要求”,多CPU、对应用响应时间无要求的中、大型应用。举例:后台处理、科学计算。
--缺点:应用响应时间可能较长- 并发处理器:
--适用情况:“对响应时间有高要求”,多CPU、对应用响应时间有较高要求的中、大型应用。举例:Web服务器/应用服务器、电信交换、集成开发环境。
2.2 垃圾收集器
下面介绍一下HotSpot(JDK 7)虚拟机提供的几种垃圾收集器,用户可以根据自己的需求组合出各个年代使用的收集器。
1.Serial&Serial Old
Serial和Serial Old收集器是最基本最古老的收集器,是一个单线程收集器,并且在它进行垃圾收集时,必须暂停所有用户线程。Serial收集器是针对新生代的收集器,采用的是Copying算法,Serial Old收集器是针对老年代的收集器,采用的是Mark-Compact算法。它的优点是实现简单高效,但是缺点是会给用户带来停顿。
2.ParNew
ParNew收集器是Serial收集器的多线程版本,使用多个线程进行垃圾收集。
3.Parallel Scavenge
Parallel Scavenge收集器是一个新生代的多线程收集器(并行收集器),其采用的是Copying算法,该收集器与前两个收集器有所不同,它主要是为了达到一个可控的吞吐量。
4.Parallel Old
Parallel Old是Parallel Scavenge收集器的老年代版本(并行收集器),使用多线程和Mark-Compact算法。
5.CMS
CMS(Current Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,它是一种并发收集器,采用的是Mark-Sweep算法。
6.G1
G1收集器是当今收集器技术发展最前沿的成果,它是一款面向服务端应用的收集器,它能充分利用多CPU、多核环境。因此它是一款并行与并发收集器,并且它能建立可预测的停顿时间模型。
最后介绍一下有关收集器设置的JVM常见配置方式:
- -XX:+UseSerialGC: //设置串行收集器
- -XX:+UseParallelGC: //设置并行收集器
- -XX:+UseParalledlOldGC: //设置并行年老代收集器
- -XX:+UseConcMarkSweepGC: //设置并发收集器
- //并行收集器设置
- -XX:ParallelGCThreads=n: //设置并行收集器收集时使用的CPU数,并行收集线程数
- -XX:MaxGCPauseMillis=n: //设置并行收集最大暂停时间
- -XX:GCTimeRatio=n: //设置垃圾回收时间占程序运行时间的百分比,公式为1/(1+n)
- //并发收集器设置
- -XX:+CMSIncrementalMode: //设置为增量模式。适用于单CPU情况
- -XX:ParallelGCThreads=n: //设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数
2.3 整理下新生代和老年代的收集器:
新生代收集器:
Serial (-XX:+UseSerialGC)
ParNew(-XX:+UseParNewGC)
ParallelScavenge(-XX:+UseParallelGC)
G1 收集器
老年代收集器:
SerialOld(-XX:+UseSerialOldGC)
ParallelOld(-XX:+UseParallelOldGC)
CMS(-XX:+UseConcMarkSweepGC)
G1 收集器
2.4 JVM小工具
在${JAVA_HOME}/bin/目录下Sun/Oracle给我们提供了一些处理应用程序性能问题、定位故障的工具, 包含
bin 描述 功能 jps 打印Hotspot VM进程 VMID、JVM参数、 main()
函数参数、主类名/Jar路径jstat 查看Hotspot VM 运行时信息 类加载、内存、GC[可分代查看]、JIT编译 jinfo 查看和修改虚拟机各项配置 -flag name=value
jmap heapdump: 生成VM堆转储快照、查询finalize执行队列、Java堆和永久代详细信息 jmap -dump:live,format=b,file=heap.bin [VMID]
jstack 查看VM当前时刻的线程快照: 当前VM内每一条线程正在执行的方法堆栈集合 Thread.getAllStackTraces()
提供了类似的功能javap 查看经javac之后产生的JVM字节码代码 自动解析 .class
文件, 避免了去理解class文件格式以及手动解析class文件内容jcmd 一个多功能工具, 可以用来导出堆, 查看Java进程、导出线程信息、 执行GC、查看性能相关数据等 几乎集合了jps、jstat、jinfo、jmap、jstack所有功能 jconsole 基于JMX的可视化监视、管理工具 可以查看内存、线程、类、CPU信息, 以及对JMX MBean进行管理 jvisualvm JDK中最强大运行监视和故障处理工具 可以监控内存泄露、跟踪垃圾回收、执行时内存分析、CPU分析、线程分析…
VM常用参数整理
参数 描述 -Xms
最小堆大小 -Xmx
最大堆大小 -Xmn
新生代大小 -XX:PermSize
永久代大小 -XX:MaxPermSize
永久代最大大小 -XX:+PrintGC
输出GC日志 -verbose:gc
- -XX:+PrintGCDetails
输出GC的详细日志 -XX:+PrintGCTimeStamps
输出GC时间戳(以基准时间的形式) -XX:+PrintHeapAtGC
在进行GC的前后打印出堆的信息 -Xloggc:/path/gc.log
日志文件的输出路径 -XX:+PrintGCApplicationStoppedTime
打印由GC产生的停顿时间