JVM 基础知识 GC

GC参数


打印GC 简要信息(二选一,等价):-verbose:gc,-XX:+printGC,打印信息如下:

–[GC 4790K->374K(15872K), 0.0001606 secs]
–[GC 4790K->374K(15872K), 0.0001474 secs]
–[GC 4790K->374K(15872K), 0.0001563 secs]
–[GC 4790K->374K(15872K), 0.0001682 secs]

打印GC详细信息:-XX:+PrintGCDetails,打印信息如下:

–Heap
–defnew generation total 13824K, used 11223K [0x27e80000, 0x28d80000, 0x28d80000)
新生代,包含Eden(伊甸 ,2个survivor(幸存区:from,to),总空间大小为Eden+1个survivor
– edenspace 12288K, 91% used [0x27e80000, 0x28975f20, 0x28a80000)
Eden :对象诞生的地方
– from space 1536K, 0% used [0x28a80000, 0x28a80000, 0x28c00000)
survivor_1:Eden对象gc没有被回收,进入survivor,2个survivor为ping pong结构
– to space 1536K, 0% used [0x28c00000, 0x28c00000, 0x28d80000)
survivor_2
–tenured generation total 5120K, used 0K [0x28d80000, 0x29280000, 0x34680000)
老年代:survivor区的数据多次gc没有被回收则进入老年代
– the space 5120K, 0% used [0x28d80000, 0x28d80000, 0x28d80200, 0x29280000)
–compacting perm gen total 12288K, used 142K [0x34680000, 0x35280000, 0x38680000)
– the space 12288K, 1% used [0x34680000, 0x346a3a90, 0x346a3c00, 0x35280000)
– rospace 10240K, 44% used [0x38680000, 0x38af73f0, 0x38af7400, 0x39080000)
read only的永久代,用于共享一些数据
– rwspace 12288K, 52% used [0x39080000, 0x396cdd28, 0x396cde00, 0x39c80000)
read write的永久代, 用于共享一些数据

打印GC时间戳:n-XX:+PrintGCTimeStamps,打印信息如下:

–[GC[DefNew: 4416K->0K(4928K), 0.0001897 secs] 4790K->374K(15872K), 0.0002232 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

指定gc log输出位置:n-Xloggc:log/gc.log

每次gc前后,打印heap信息:n-XX:+PrintHeapAtGC,打印信息如下(具体含义参考-XX:+PrintGCDetails的输出 ):

Heap before GC invocations=0 (full 0):
 def new generation total 3072K, used 2752K [0x33c80000, 0x33fd0000, 0x33fd0000)
  edenspace 2752K, 100% used [0x33c80000, 0x33f30000, 0x33f30000)
  from space 320K, 0% used [0x33f30000, 0x33f30000, 0x33f80000)
  to space 320K, 0% used [0x33f80000, 0x33f80000, 0x33fd0000)
 tenured generation total 6848K, used 0K [0x33fd0000, 0x34680000, 0x34680000)
  the space 6848K, 0% used [0x33fd0000, 0x33fd0000, 0x33fd0200, 0x34680000)
 compacting perm gen total 12288K, used 143K [0x34680000, 0x35280000, 0x38680000)
  the space 12288K, 1% used [0x34680000, 0x346a3c58, 0x346a3e00, 0x35280000)
  rospace 10240K, 44% used [0x38680000, 0x38af73f0, 0x38af7400, 0x39080000)
  rwspace 12288K, 52% used [0x39080000, 0x396cdd28, 0x396cde00, 0x39c80000)
[GC[DefNew: 2752K->320K(3072K), 0.0014296 secs] 2752K->377K(9920K), 0.0014604 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap after GC invocations=1 (full 0):
 def new generation total 3072K, used 320K [0x33c80000, 0x33fd0000, 0x33fd0000)
  edenspace 2752K, 0% used [0x33c80000, 0x33c80000, 0x33f30000)
  from space 320K, 100% used [0x33f80000, 0x33fd0000, 0x33fd0000)
  to space 320K, 0% used [0x33f30000, 0x33f30000, 0x33f80000)
 tenured generation total 6848K, used 57K [0x33fd0000, 0x34680000, 0x34680000)
  the space 6848K, 0% used [0x33fd0000, 0x33fde458, 0x33fde600, 0x34680000)
 compacting perm gen total 12288K, used 143K [0x34680000, 0x35280000, 0x38680000)
  the space 12288K, 1% used [0x34680000, 0x346a3c58, 0x346a3e00, 0x35280000)
  rospace 10240K, 44% used [0x38680000, 0x38af73f0, 0x38af7400, 0x39080000)
  rwspace 12288K, 52% used [0x39080000, 0x396cdd28, 0x396cde00, 0x39c80000)

监控类的加载:-XX:+TraceClassLoading,打印信息如下:

[Loaded java.lang.Objectfrom shared objects file]
[Loaded java.io.Serializablefrom shared objects file]
[Loaded java.lang.Comparablefrom shared objects file]
[Loaded java.lang.CharSequencefrom shared objects file]
[Loaded java.lang.Stringfrom shared objects file]
[Loaded java.lang.reflect.GenericDeclarationfrom shared objects file]
[Loaded java.lang.reflect.Typefrom shared objects file]

如果是命令行启动,显示类的实例信息:-XX:+PrintClassHistogram,–按下Ctrl+Break后,打印类的信息:
num instances(实例数量) bytes(占用内存大小) class name(类名)
1 890617 470266000 [B(byte数组)
2 890643 21375432 java.util.HashMap$Node
3 890608 14249728 java.lang.Long
4 13 8389712 [Ljava.util.HashMap$Node;
5 2062 371680 [C(char数组0)
6 463 41904 java.lang.Class

最大堆:-Xmx,最小堆:–Xms

  • -Xmx和 –Xms应该保持一个什么关系,可以让系统的性能尽可能的好呢?
xms设置过小,会导致启动慢(GC更频繁,并且极有可能产生full gc,jvm会尽量将heap保持在xms以下,类似eclipse默认启动慢的调优)
xms接近xmx未发现有什么问题,问题应该就是浪费内存吧。

  • 如果你要做一个Java的桌面产品,需要绑定JRE,但是JRE又很大,你如何做一下JRE的瘦身呢?
编写脚本,剔除rt.jar中不需要的class

设置新生代大小:-Xmn

老年代和新生代的比值:-XX:NewRatio (老年代(不包含永久区)和 新生代(eden+2*survivor)的比值,例如设置为4,则新生代内存和老年代的内存比例为1:4)

两个Survivor区和eden的比:-XX:SurvivorRatio(8表示 两个Survivor :eden=2:8,即一个Survivor占年轻代的1/10)

OOM时导出堆到文件:-XX:+HeapDumpOnOutOfMemoryError

导出OOM的路径:-XX:+HeapDumpPath:举例来说:
n-Xmx20m -Xms5m -XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=d:/a.dump

在OOM时,执行一个脚本:-XX:OnOutOfMemoryError:举例来说:
-XX:OnOutOfMemoryError=D:/tools/jdk1.7_40/bin/printstack.bat %p (–当程序OOM时,在D:/a.txt中将会生成线程的dump,printstack.bat %p 等价于 D:/tools/jdk1.7_40/bin/jstack -F %1 > D:/a.txt)

参数配置总结:
根据实际事情调整新生代和幸存代的大小
官方推荐新生代占堆的3/8
幸存代占新生代的1/10
在OOM时,记得Dump出堆,确保可以排查现场问题

设置永久区的初始空间和最大空间:-XX:PermSize -XX:MaxPermSize

栈大小分配:-Xss


GC算法和种类

  1. 引用计数法

          问题: 加减法影响性能;很难处理循环引用

  1. 标记-清除算法(现代GC算法的基础)
  2. 标记-压缩算法(内存重排,防止碎片化,老年代使用)
  3. 复制算法(比标记-清除高效;不适用大内存区域(老年代),JVM中的新生代使用复制算法)

分代思想:短命对象->新生代,大对象(新生代中的Eden,survivor放不下),长命对象归为老年代;少量对象存活->复制算法,大量对象存活->标记-清除,标记-压缩

stop the world:危害:
  • 无响应;
  • 引发HA系统准备切换,原主机GC结束后重新提供服务,两个服务会导致问题。


JVM GC收集器

串行收集器:SerialGC
最古老,最稳定;
效率高(单核效率);
stw时间长;
-XX:+UseSerialGC
gc算法:新生代、老年代使用串行回收
              新生代复制算法
              老年代标记-压缩

日志格式:
0.844: [GC 0.844: [ DefNew: 17472K->2176K(19648K), 0.0188339 secs] 17472K->2375K(63360K), 0.0189186 secs] [Times: user=0.01 sys=0.00, real=0.02 secs]
8.259: [Full GC 8.259: [ Tenured: 43711K->40302K(43712K), 0.2960477 secs] 63350K->40302K(63360K), [Perm : 17836K->17836K(32768K)], 0.2961554 secs] [Times: user=0.28 sys=0.02, real=0.30 secs]

并行收集器:ParNewGC(仅是新生代的垃圾收集器)
Serial收集器新生代的并行版本
-XX:ParallelGCThreads 限制线程数量
–-XX:+UseParNewGC
gc算法同SerialGC

日志格式:
0.834: [GC 0.834: [ ParNew: 13184K->1600K(14784K), 0.0092203 secs] 13184K->1921K(63936K), 0.0093401 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

并行收集器:ParallelGC
类似ParNew,更加关注吞吐量(cpu利用率更高,但是stw时间单次更长),默认会动态调整Eden:survivor1:survivor2的值
-XX:+UseParallelGC:使用Parallel收集器+ 老年代串行
-XX:+UseParallelOldGC:使用Parallel收集器+ 并行老年代
gc算法同SerialGC

日志格式:
1.500: [Full GC [ PSYoungGen: 2682K->0K(19136K)] [ ParOldGen: 28035K->30437K(43712K)] 30717K->30437K(62848K) [ PSPermGen: 10943K->10928K(32768K)], 0.2902791 secs] [Times: user=1.44 sys=0.03, real=0.30 secs]

-XX:MaxGCPauseMills:最大停顿时间,单位毫秒,GC尽力保证回收时间不超过设定值
-XX:GCTimeRatio:0-100的取值范围,垃圾收集时间占总时间的比,默认99,即最大允许1%时间做GC
这两个参数是矛盾的。因为停顿时间和吞吐量(cpu利用率)不可能同时调优

并发标记清除收集器:ConcMarkSweepGC(CMS)
并发:与用户现场一起执行,并发阶段会降低用户线程的执行效率(抢占cpu资源)
-XX:+UseConcMarkSweepGC
gc算法:新生代使用parNew、老年代使用CMS
              新生代复制算法
              老年代标记-清除(并发的时候不能压缩,gc后内存会碎片化)



日志格式:
1.662: [GC [1 CMS-initial-mark: 28122K(49152K)] 29959K(63936K), 0.0046877 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
1.666: [CMS-concurrent-mark-start]
1.699: [ CMS-concurrent-mark: 0.033/0.033 secs] [Times: user=0.25 sys=0.00, real=0.03 secs]
1.699: [CMS-concurrent-preclean-start]
1.700: [ CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
1.700: [GC[YG occupancy: 1837 K (14784 K)]1.700: [Rescan (parallel) , 0.0009330 secs]1.701: [weak refs processing, 0.0000180 secs] [1 CMS-remark: 28122K(49152K)] 29959K(63936K), 0.0010248 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
1.702: [CMS-concurrent-sweep-start]
1.739: [ CMS-concurrent-sweep: 0.035/0.037 secs] [Times: user=0.11 sys=0.02, real=0.05 secs]
1.739: [CMS-concurrent-reset-start]
1.741: [ CMS-concurrent-reset: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

尽可能降低停顿
会影响系统整体吞吐量和性能
-XX:CMSInitiatingOccupancyFraction:设置触发GC的阈值
因为和用户线程一起运行,不能在空间快满时再清理 ,如果不幸内存预留空间不够,就会引起concurrent mode failure:
33.348: [Full GC 33.348: [CMS33.357: [CMS-concurrent-sweep: 0.035/0.036 secs] [Times: user=0.11 sys=0.03, real=0.03 secs]
 (concurrent mode failure): 47066K->39901K(49152K), 0.3896802 secs] 60771K->39901K(63936K), [CMS Perm : 22529K->22529K(32768K)], 0.3897989 secs] [Times: user=0.39 sys=0.00, real=0.39 secs]
使用串行收集器作为后备
-XX:+UseCMSCompactAtFullCollectionFull:GC后,进行一次整理,整理过程是独占的,会引起停顿时间变长
-XX:CMSFullGCsBeforeCompaction:设置进行几次Full GC后,进行一次碎片整理
-XX:ParallelCMSThreads:设定CMS的线程数量

CMS调优总结

Garbage-First收集器:G1GC
类似CMS,并发回收,内存进行分块(分布式),评估每块的回收价值,由高到低进行回收,核心是减少stw时间。
原理参考 JDK1.7的G1收集器, 深入JVM之G1收集器




Tomcat实例

Jmeter:建立10个线程,每个线程请求Tomcat 1000次 共10000次请求


堆大小对gc的影响

  • JDK6:使用32M堆处理请求
          –set CATALINA_OPTS=-server -Xloggc:gc.log-XX:+PrintGCDetails-Xmx32M -Xms32M -XX:+HeapDumpOnOutOfMemoryError-XX:+UseSerialGC-XX:PermSize=32M

          

          

  • JDK6:使用最大堆512M堆处理请求(FULL GC很少,基本上是Minor GC
          –set CATALINA_OPTS=-Xmx512m -XX:MaxPermSize=32M -Xloggc:gc.log-XX:+PrintGCDetails

          

          

  • JDK6:使用最大堆512M堆处理请求(GC数量减少 大部分是Minor GC)
          –set CATALINA_OPTS=-Xmx512m -Xms64m -XX:MaxPermSize=32M -Xloggc:gc.log-XX:+PrintGCDetails

          

          

  • JDK6:使用最大堆512M堆处理请求(64bit 默认server模式,server模式默认使用Parallel GC,此处应该是32bit jdk;GC压力原本不大,修改GC方式影响很小)
          –set CATALINA_OPTS=-Xmx512m -Xms64m -XX:MaxPermSize=32M -Xloggc:gc.log-XX:+PrintGCDetails -XX:+UseParallelGC-XX:+UseParallelOldGC-XX:ParallelGCThreads=4

          



gc算法对gc的影响

  • Serial
          –set CATALINA_OPTS=-Xmx40m -Xms40m -XX:MaxPermSize=32M -Xloggc:gc.log-XX:+PrintGCDetails

          

  • Parallel(相较Serial有提升)
          –set CATALINA_OPTS=-Xmx40m -Xms40m -XX:MaxPermSize=32M -Xloggc:gc.log-XX:+PrintGCDetails -XX:+UseParallelOldGC-XX:ParallelGCThreads=4

          

  • ParNew+Serial(介于Serial和Parallel之间)
          –set CATALINA_OPTS=-Xmx40m -Xms40m -XX:MaxPermSize=32M -Xloggc:gc.log-XX:+PrintGCDetails -XX:+UseParNewGC
          
          


jdk版本对性能的影响

  • jdk6(不加任何参数)

          

  • jdk7(不加任何参数,相较jdk6有10%提升

          


























你可能感兴趣的:(Java)