jvm-gc二

由于heap中对象的存活时间差异很大,如果每一次都是无差别的进行gc,效率会很差。将heap按照对象大小、存活时间划分出不同的区域,针对不同的区域使用不同的gc算法可以提高效率。

年轻代的对象存活率低可以采用复制算法,老年代的对象或是存活率高的对象,或是大对象,这些对象使用复制算法进行gc成本太高,所以老年代的gc可以采用标记-清除(可以选择是否进行压缩)。

ps:虽然每个区域的垃圾收集算法较为固定,但每个垃圾收集器的工作逻辑又有差别(比如单线程、多线程,并发、并行等)。

Serial

-XX:+UseSerialGC,显示指定Serial新生代垃圾收集器(Serial Old是Serial的老年代版本)

ParNew

-XX:+UseParNewGC,显示指定ParNew新生代垃圾收集器,ParNew是Serial的多线程版本(通过-XX:+UseConcMarkSweepGC设置CMS为老年代垃圾收集器后,新生代默认就为ParNew);

CMS

  • 初始标记:STW,单线程,由于是从GCRoot寻找直达的对象,速度快;
  • 并发标记:与应用线程一起运行,是CMS最主要的工作阶段,通过直达对象,扫描全部的对象,进行标记;
  • 重新标记:STW,修正并发标记时由于应用程序还在并发运行产生的对象的修改(标记在可达路径上新增的对象),多线程,速度快,需要全局停顿;
  • 并发清除:与应用程序一起运行。CMS主要关注低延迟,因而采用并发方式,清理垃圾时,应用程序还在运行。如果开启压缩算法,则涉及到要移动应用程序的存活对象,此时不停顿,是很难处理的,一般需要停顿下,移动存活对象,再让应用程序继续运行,但这样停顿时间变长,延迟变大

-XX:+UseConcMarkSweepGC,使用CMS收集器;

-XX:+ UseCMSCompactAtFullCollection,Full GC后,进行一次碎片整理;整理过程是独占的,会引起停顿时间变长;

-XX:+CMSFullGCsBeforeCompaction=n,设置进行n次Full GC后,进行一次碎片整理;

-XX:ParallelCMSThreads=n,在进行并行GC的时候,用于GC的线程数,比如ParNew在对新生代gc时会STW,这是会有n个线程并行gc;

-XX:ConcGCThreads=n,设定CMS并发的线程数量(如果指定了ConcGCThreads=n,则除初始标记外,其余阶段都会使用n个线程,如果没有指定ConcGCThreads,则ConcGCThreads = (ParallelGCThreads + 3)/4);

-XX:CMSInitiatingOccupancyFraction=n,老年代使用n%后触发CMS垃圾回收;

 1    /** ParallelGCThreads的取值策略:
 2     ①如果用户显示指定了ParallelGCThreads,则使用用户指定的值。
 3     ②否则,需要根据实际的CPU所能够支持的线程数来计算ParallelGCThreads的值,计算方法见步骤③和步骤④。
 4     ③如果物理CPU所能够支持线程数小于8,则ParallelGCThreads的值为CPU所支持的线程数。这里的阀值为8,是因为JVM中调用nof_parallel_worker_threads接口所传入的switch_pt的值均为8。
 5     ④如果物理CPU所能够支持线程数大于8,则ParallelGCThreads的值为8加上一个调整值,调整值的计算方式为:物理CPU所支持的线程数减去8所得值的5/8或者5/16,JVM会根据实际的情况来选择具体是乘以5/8还是5/16。
 6     比如,在64线程的x86 CPU上,如果用户未指定ParallelGCThreads的值,则默认的计算方式为:ParallelGCThreads = 8 + (64 - 8) * (5/8) = 8 + 35 = 43。
 7     */
 8 unsigned int VM_Version::calc_parallel_worker_threads() {
 9   unsigned int result;
10   if (is_M_series()) {
11     // for now, use same gc thread calculation for M-series as for niagara-plus
12     // in future, we may want to tweak parameters for nof_parallel_worker_thread
13     result = nof_parallel_worker_threads(5, 16, 8);
14   } else if (is_niagara_plus()) {
15     result = nof_parallel_worker_threads(5, 16, 8);
16   } else {
17     result = nof_parallel_worker_threads(5, 8, 8);
18   }
19   return result;
20 }
21
22 unsigned int Abstract_VM_Version::parallel_worker_threads() {
23   if (!_parallel_worker_threads_initialized) {
24     if (FLAG_IS_DEFAULT(ParallelGCThreads)) {
25       _parallel_worker_threads = VM_Version::calc_parallel_worker_threads();
26     } else {
27       _parallel_worker_threads = ParallelGCThreads;
28     }
29     _parallel_worker_threads_initialized = true;
30   }
31   return _parallel_worker_threads;
32 }
33
34 unsigned int Abstract_VM_Version::calc_parallel_worker_threads() {
35   return nof_parallel_worker_threads(5, 8, 8);
36 }
37
38 unsigned int Abstract_VM_Version::nof_parallel_worker_threads(
39                                                       unsigned int num,
40                                                       unsigned int den,
41                                                       unsigned int switch_pt) {
42   if (FLAG_IS_DEFAULT(ParallelGCThreads)) {
43     assert(ParallelGCThreads == 0, "Default ParallelGCThreads is not 0");
44     // For very large machines, there are diminishing returns
45     // for large numbers of worker threads.  Instead of
46     // hogging the whole system, use a fraction of the workers for every
47     // processor after the first 8.  For example, on a 72 cpu machine
48     // and a chosen fraction of 5/8
49     // use 8 + (72 - 8) * (5/8) == 48 worker threads.
50     unsigned int ncpus = (unsigned int) os::active_processor_count();
51     return (ncpus <= switch_pt) ?
52            ncpus :
53           (switch_pt + ((ncpus - switch_pt) * num) / den);
54   } else {
55     return ParallelGCThreads;
56   }
57 } 

我们知道CMS初始标记只标记了GCRoot,并发标记是与应用线程并发进行的,如果某对象a刚被标记就变成了垃圾(没有引用指向它),CMS是没有任何办法的,这就产生了“浮动垃圾”,“浮动垃圾”只能等到下一次gc进行回收。

由于并发标记、并发清除都是与应用程序线程并发进行的,这就会产生一种危险,即并发gc过程中应用程序产生了新对象,而此时gc还未完成,还未释放出足够的空间存放这个新对象。可以通过UseCMSInitiatingOccupancyOnly来指定当老年代使用了多少空间后触发full gc,如果此后在gc过程中仍然出现了空间不足的情况,CMS就会出现“Concurrent Mode Failure”,这时jvm会启动后备预案,即临时启动Serial Old收集器来重新进行老年代的垃圾收集。

不管是minor gc还是major gc,都会涉及到老年代和新生代的gc root。在进行新生代垃圾回收时,GC root可能在老年代中,而GC root可达的对象在新生代中,这种情况对于新生代垃圾回收来说,老年代对象指向新生代对象的引用才是事实上的GC root。而对于GC root在新生代,GC root可达的对象在老年代中,这种情况对于新生代垃圾回收来说,老年代中的可达对象会被忽略。

参考:http://www.cnblogs.com/ityouknow/p/5614961.html

  http://blog.csdn.net/bdx_hadoop_opt/article/details/38021209

  https://plumbr.eu/blog/garbage-collection/minor-gc-vs-major-gc-vs-full-gc

  http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html

时间: 2024-08-01 12:03:24

jvm-gc二的相关文章

深入JVM虚拟机(二) JVM运行机制

深入JVM虚拟机(二) JVM运行机制 1 JVM运行机制 1.1 JVM启动流程 JVM是Java程序运行的环境,同时是一个操作系统的一个应用程序进程,因此它有自己的生命周期,也有自己的代码和数据空间.JVM工作原理和特点主要是指操作系统装入JVM,是通过jdk中Java.exe来完成通过下面4步来完成JVM环境. 1.创建JVM装载环境和配置. 2.装载JVM.dll. 3.初始化JVM.dll并挂界到JNIENV(JNI调用接口)实例. 4.调用JNIEnv实例装载并处理class类. J

【甘道夫】HBase随机宕机事件处理 &amp; JVM GC回顾

一.引言 本文记录了困扰团队两周的HBase随机宕机事件的解决方案,并回顾了JVM GC调优基础知识,供各位参考. 欢迎转载,请注明出处: http://blog.csdn.net/u010967382/article/details/42394031 二.实验环境 16台虚拟机,每台4G内存,1核CPU,400G硬盘 Ubuntu 14.04 LTS (GNU/Linux 3.13.0-29-generic x86_64) CDH5.2.0套装(包括相应版本的Hadoop,HIVE,Hbase

Java性能优化之JVM GC(垃圾回收机制)

Java的性能优化,整理出一篇文章,供以后温故知新. JVM GC(垃圾回收机制) 在学习Java GC 之前,我们需要记住一个单词:stop-the-world .它会在任何一种GC算法中发生.stop-the-world 意味着JVM因为需要执行GC而停止了应用程序的执行.当stop-the-world 发生时,除GC所需的线程外,所有的线程都进入等待状态,直到GC任务完成.GC优化很多时候就是减少stop-the-world 的发生. JVM GC回收哪个区域内的垃圾? 需要注意的是,JV

JVM GC调优一则--增大Eden Space提高性能

缘起 线上有Tomcat升级到7.0.52版,然后有应用的JVM FullGC变频繁,在高峰期socket连接数,Cpu使用率都暴增. 思路 思路是Tomcat本身的代码应该是没有问题的,有问题的可能是应用代码升级,或者环境改变了,总之Tomcat的优先级排在最后. 先把应用的heap dump下来分析下: jmap -dump:format=b,file=path pid 用IBM的Heap Analyser分析,发现dubbo rpc调用的RpcInvocation对象和taglibs的Si

【转载】Java性能优化之JVM GC(垃圾回收机制)

章来源:https://zhuanlan.zhihu.com/p/25539690 Java的性能优化,整理出一篇文章,供以后温故知新. JVM GC(垃圾回收机制) 在学习Java GC 之前,我们需要记住一个单词:stop-the-world .它会在任何一种GC算法中发生.stop-the-world 意味着JVM因为需要执行GC而停止了应用程序的执行.当stop-the-world 发生时,除GC所需的线程外,所有的线程都进入等待状态,直到GC任务完成.GC优化很多时候就是减少stop-

深入浅出 JVM GC(3)

# 前言 在 深入浅出 JVM GC(2) 中,我们介绍了一些 GC 算法,GC 名词,同时也留下了一个问题,就是每个 GC 收集器的具体作用.有哪些 GC 收集器呢? Serial 串行收集器(只适用于堆内存 256M 以下的 JVM ) ParNew 并行收集器(Serial 收集器的多线程版本) Parallel Scavenge (PS 收集器,该收集器以吞吐量为主要目的,是1.8的默认 GC) CMS 收集器(该收集器全称 Concurrent Mark Sweep,是一种关注最短停顿

深入浅出 JVM GC(2)

# 前言 在 深入浅出 JVM GC(1) 中,限于上篇文章的篇幅,我们留下了一个问题 : 如何回收? 这篇文章将重点讲述这个问题. 在上篇文章中,我们也列出了一些大纲,今天我们就按照那个大纲来逐个讲解.在此,我将大纲复制过来. 垃圾回收算法 标记清除算法 复制算法 标记整理算法 分代收集算法(堆如何分代) 有哪些垃圾收集器 Serial 串行收集器(只适用于堆内存256m 以下的 JVM ) ParNew 并行收集器(Serial 收集器的多线程版本) Parallel Scavenge (P

JVM(十二):方法调用

JVM(十二):方法调用 在 JVM(七):JVM内存结构 中,我们说到了方法执行在何种内存结构上执行:Java 方法活动在虚拟机栈中的栈帧上,栈帧的具体结构在内存结构中已经详细讲解过了,下面就让我们来看一下 方法是如何调用的. 方法调用 首先,我们要明白一个基础性概念:方法调用并不是方法执行.其只是确定该调用哪一个方法而已(多态的影响,选择方法的不同版本).并且因为 Java 调用的动态性,有些方法需要在类加载阶段动态解析,这也为 JVM 解析符号引用成直接引用提供了难度. 解析 在 JVM(

[转]JVM系列二:GC策略&amp;内存申请、对象衰老

原文地址:http://www.cnblogs.com/redcreen/archive/2011/05/04/2037056.html JVM里的GC(Garbage Collection)的算法有很多种,如标记清除收集器,压缩收集器,分代收集器等等,详见HotSpot VM GC 的种类 现在比较常用的是分代收集(generational collection,也是SUN VM使用的,J2SE1.2之后引入),即将内存分为几个区域,将不同生命周期的对象放在不同区域里:young genera

JVM系列二:GC策略&amp;内存申请、对象衰老

JVM里的GC(Garbage Collection)的算法有很多种,如标记清除收集器,压缩收集器,分代收集器等等,详见HotSpot VM GC 的种类 现在比较常用的是分代收集(generational collection,也是SUN VM使用的,J2SE1.2之后引入),即将内存分为几个区域,将不同生命周期的对象放在不同区域里:young generation,tenured generation和permanet generation.绝大部分的objec被分配在young gener