|NO.Z.00063|——————————|BigDataEnd|——|Hadoop&MapReduce.V34|——|Hadoop.v34|NamenodeFullGC-FullGC的影响|日志分析|
一、Namenode Full GC
~~~ [NamenodeFullGC-FullGC的影响]
~~~ [NamenodeFullGC-FullGC的日志分析]
二、JVM堆内存
### --- JVM堆内存
~~~ JVM内存划分为堆内存和非堆内存,堆内存分为年轻代(Young Generation)、
~~~ 老年代(OldGeneration),非堆内存就一个永久代(Permanent Generation)。
~~~ 年轻代又分为Eden和Survivor区。Survivor区由FromSpace和ToSpace组成。
~~~ Eden区占大容量,Survivor两个区占小容量,默认比例是8:1:1。
~~~ 堆内存用途:存放的是对象,垃圾收集器就是收集这些对象,然后根据GC算法回收。
~~~ 非堆内存用途:永久代,也称为方法区,存储程序运行时长期存活的对象,
~~~ 比如类的元数据、方法、常量、属性等。
### --- 补充:
~~~ JDK1.8版本废弃了永久代,替代的是元空间(MetaSpace),元空间与永久代上类似,
~~~ 都是方法区的实现,他们最大区别是:元空间并不在JVM中,而是使用本地内存。
三、对象分代
### --- 对象分代
~~~ 新生成的对象首先放到年轻代Eden区,
~~~ 当Eden空间满了,触发Minor GC,存活下来的对象移动到Survivor0区,
~~~ Survivor0区满后触发执行Minor GC,Survivor0区存活对象移动到Suvivor1区,
~~~ 这样保证了一段时间内总有一个survivor区为空。
~~~ 经过多次Minor GC仍然存活的对象移动到老年代。
~~~ 老年代存储长期存活的对象,占满时会触发Major GC(Full GC),
~~~ GC期间会停止所有线程等待GC完成,所以对响应要求高的应用尽量减少发生Major GC,避免响应超时。
~~~ Minor GC : 清理年轻代
~~~ Major GC(Full GC) : 清理老年代,清理整个堆空间,会停止应用所有线程。
四、Jstat
### --- 查看当前jvm内存使用以及垃圾回收情况
[root@linux121 ~]# jps
8960 NameNode
### --- 显示pid是58563的垃圾回收堆的行为统计
[root@linux121 ~]# jstat -gc -t 8960 1s
Timestamp S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
1489.6 2240.0 2240.0 1003.8 0.0 17984.0 8890.8 44796.0 31671.0 41392.0 40564.9 5040.0 4843.1 36 0.371 2 0.414 0.785
1490.6 2240.0 2240.0 1003.8 0.0 17984.0 8890.8 44796.0 31671.0 41392.0 40564.9 5040.0 4843.1 36 0.371 2 0.414 0.785
1491.6 2240.0 2240.0 1003.8 0.0 17984.0 8890.8 44796.0 31671.0 41392.0 40564.9 5040.0 4843.1 36 0.371 2 0.414 0.785
1492.6 2240.0 2240.0 1003.8 0.0 17984.0 8973.3 44796.0 31671.0 41392.0 40564.9 5040.0 4843.1 36 0.371 2 0.414 0.785
1493.6 2240.0 2240.0 1003.8 0.0 17984.0 8980.4 44796.0 31671.0 41392.0 40564.9 5040.0 4843.1 36 0.371 2 0.414 0.785
1494.6 2240.0 2240.0 1003.8 0.0 17984.0 8980.4 44796.0 31671.0 41392.0 40564.9 5040.0 4843.1 36 0.371 2 0.414 0.785
1495.6 2240.0 2240.0 1003.8 0.0 17984.0 8980.4 44796.0 31671.0 41392.0 40564.9 5040.0 4843.1 36 0.371 2 0.414 0.785
1496.6 2240.0 2240.0 1003.8 0.0 17984.0 9021.4 44796.0 31671.0 41392.0 40564.9 5040.0 4843.1 36 0.371 2 0.414 0.785
1497.6 2240.0 2240.0 1003.8 0.0 17984.0 9021.4 44796.0 31671.0 41392.0 40564.9 5040.0 4843.1 36 0.371 2 0.414 0.785
1498.6 2240.0 2240.0 1003.8 0.0 17984.0 9025.0 44796.0 31671.0 41392.0 40564.9 5040.0 4843.1 36 0.371 2 0.414 0.785
1499.6 2240.0 2240.0 1003.8 0.0 17984.0 9025.1 44796.0 31671.0 41392.0 40564.9 5040.0 4843.1 36 0.371 2 0.414 0.785
1500.6 2240.0 2240.0 1003.8 0.0 17984.0 9025.1 44796.0 31671.0 41392.0 40564.9 5040.0 4843.1 36 0.371 2 0.414 0.785
1501.6 2240.0 2240.0 1003.8 0.0 17984.0 9058.0 44796.0 31671.0 41392.0 40564.9 5040.0 4843.1 36 0.371 2 0.414 0.785
1502.6 2240.0 2240.0 1003.8 0.0 17984.0 9058.0 44796.0 31671.0 41392.0 40564.9 5040.0 4843.1 36 0.371 2 0.414 0.785
1503.6 2240.0 2240.0 1003.8 0.0 17984.0 9061.5 44796.0 31671.0 41392.0 40564.9 5040.0 4843.1 36 0.371 2 0.414 0.785
### --- 结果解释:
~~~ # C即Capacity 总容量,U即Used 已使用的容量
S0C: // 当前survivor0区容量(kB)。
S1C: // 当前survivor1区容量(kB)。
S0U: // survivor0区已使用的容量(KB)
S1U: // survivor1区已使用的容量(KB)
EC: // Eden区的总容量(KB)
EU: // 当前Eden区已使用的容量(KB)
OC: // Old空间容量(kB)。
OU: // Old区已使用的容量(KB)
MC: // Metaspace空间容量(KB)
MU: // Metacspace使用量(KB)
CCSC: // 压缩类空间容量(kB)。
CCSU: // 压缩类空间使用(kB)。
YGC: // 新生代垃圾回收次数
YGCT: // 新生代垃圾回收时间
FGC: // 老年代 full GC垃圾回收次数
FGCT: // 老年代垃圾回收时间
GCT: // 垃圾回收总消耗时间
### --- 开启HDFS GC详细日志输出
~~~ 编辑hadoop-env.sh
[root@linux121 ~]# vim /opt/yanqi/servers/hadoop-2.9.2/etc/hadoop/hadoop-env.sh
export HADOOP_LOG_DIR=/hadoop/logs/
### --- 增加JMX配置打印详细GC信息
~~~ 指定一个日志输出目录;注释掉之前的ops
~~~ 增加新的打印配置
[root@linux121 ~]# vim /opt/yanqi/servers/hadoop-2.9.2/etc/hadoop/hadoop-env.sh
# JMX配置
export HADOOP_JMX_OPTS="-Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false"
export HADOOP_NAMENODE_OPTS="-Dhadoop.security.logger=${HADOOP_SECURITY_LOGGER:-INFO,RFAS} -Dhdfs.audit.logger=${HDFS_AUDIT_LOGGER:-INFO,NullAppender}$HADOOP_NAMENODE_OPTS"
export HADOOP_DATANODE_OPTS="-Dhadoop.security.logger=ERROR,RFAS$HADOOP_DATANODE_OPTS"
export NAMENODE_OPTS="-verbose:gc -XX:+PrintGCDetails -Xloggc:${HADOOP_LOG_DIR}/logs/hadoop-gc.log \
-XX:+PrintGCDateStamps -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime \
-server -Xms150g -Xmx150g -Xmn20g -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 \
-XX:ParallelGCThreads=18 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+UseCMSCompactAtFullCollection -XX:+DisableExplicitGC -XX:+CMSParallelRemarkEnabled \
-XX:+CMSClassUnloadingEnabled -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSMaxAbortablePrecleanTime=5000 \
-XX:+UseGCLogFileRotation -XX:GCLogFileSize=20m -XX:ErrorFile=${HADOOP_LOG_DIR}/logs/hs_err.log.%p -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${HADOOP_LOG_DIR}/logs/%p.hprof\
"
export DATENODE_OPTS="-verbose:gc -XX:+PrintGCDetails -Xloggc:${HADOOP_LOG_DIR}/hadoop-gc.log \
-XX:+PrintGCDateStamps -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime \
-server -Xms15g -Xmx15g -Xmn4g -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15\
-XX:ParallelGCThreads=18 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+UseCMSCompactAtFullCollection -XX:+DisableExplicitGC -XX:+CMSParallelRemarkEnabled \
-XX:+CMSClassUnloadingEnabled -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSMaxAbortablePrecleanTime=5000 \
-XX:+UseGCLogFileRotation -XX:GCLogFileSize=20m -XX:ErrorFile=${HADOOP_LOG_DIR}/logs/hs_err.log.%p -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${HADOOP_LOG_DIR}/logs/%p.hprof\
"
export HADOOP_NAMENODE_OPTS="$NAMENODE_OPTS $HADOOP_NAMENODE_OPTS"
export HADOOP_DATANODE_OPTS="$DATENODE_OPTS $HADOOP_DATANODE_OPTS"
~~~ -Xms150g -Xmx150g :堆内存大小最大和最小都是150g
~~~ -Xmn20g :新生代大小为20g,等于eden+2*survivor,意味着老年代为150-20=130g。
~~~ -XX:SurvivorRatio=8 :Eden和Survivor的大小比值为8,意味着两个Survivor区和一个Eden区的比值为2:8,一个Survivor占整个年轻代的1/10
~~~ -XX:ParallelGCThreads=10 :设置ParNew GC的线程并行数,默认为8 +(Runtime.availableProcessors - 8) * 5/8 ,24核机器为18。
~~~ -XX:MaxTenuringThreshold=15 :设置对象在年轻代的最大年龄,超过这个年龄则会晋升到老年代
~~~ -XX:+UseParNewGC :设置新生代使用Parallel New GC
~~~ -XX:+UseConcMarkSweepGC :设置老年代使用CMS GC,当此项设置时候自动设置新生代为ParNew GC
~~~ -XX:CMSInitiatingOccupancyFraction=70 :老年代第一次占用达到该百分比时候,就会引发CMS的第一次垃圾回收周期。后继CMS GC由HotSpot自动优化计算得到。
五、GC 日志解析
### --- 日志解析:jstat命令输出
[root@linux121 ~]# jstat -gc -t 8960 1s
Timestamp S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
2865.7 2240.0 2240.0 0.0 295.9 17984.0 8002.0 44796.0 31671.0 41392.0 40672.3 5040.0 4845.9 37 0.376 2 0.414 0.791
2866.7 2240.0 2240.0 0.0 295.9 17984.0 8018.1 44796.0 31671.0 41392.0 40672.3 5040.0 4845.9 37 0.376 2 0.414 0.791
2867.8 2240.0 2240.0 0.0 295.9 17984.0 8033.4 44796.0 31671.0 41392.0 40672.3 5040.0 4845.9 37 0.376 2 0.414 0.791
2868.8 2240.0 2240.0 0.0 295.9 17984.0 8033.4 44796.0 31671.0 41392.0 40672.3 5040.0 4845.9 37 0.376 2 0.414 0.791
2869.7 2240.0 2240.0 0.0 295.9 17984.0 8040.5 44796.0 31671.0 41392.0 40672.3 5040.0 4845.9 37 0.376 2 0.414 0.791
2870.8 2240.0 2240.0 0.0 295.9 17984.0 8040.5 44796.0 31671.0 41392.0 40672.3 5040.0 4845.9 37 0.376 2 0.414 0.791
2871.8 2240.0 2240.0 0.0 295.9 17984.0 8040.5 44796.0 31671.0 41392.0 40672.3 5040.0 4845.9 37 0.376 2 0.414 0.791
### --- 查看GC日志输出
3.157: [GC (Allocation Failure) 3.157: [ParNew: 272640K->34048K(306688K),0.0844702 secs] 272640K->69574K(2063104K), 0.0845560 secs] [Times: user=0.23sys=0.03, real=0.09 secs]
4.092: [GC (Allocation Failure) 4.092: [ParNew: 306688K->34048K(306688K),0.1013723 secs] 342214K->136584K(2063104K), 0.1014307 secs] [Times: user=0.25sys=0.05, real=0.10 secs]
... cut for brevity ...
11.292: [GC (Allocation Failure) 11.292: [ParNew: 306686K->34048K(306688K),0.0857219 secs] 971599K->779148K(2063104K), 0.0857875 secs] [Times: user=0.26sys=0.04, real=0.09 secs]
12.140: [GC (Allocation Failure) 12.140: [ParNew: 306688K->34046K(306688K),0.0821774 secs] 1051788K->856120K(2063104K), 0.0822400 secs] [Times: user=0.25sys=0.03, real=0.08 secs]
12.989: [GC (Allocation Failure) 12.989: [ParNew: 306686K->34048K(306688K),0.1086667 secs] 1128760K->931412K(2063104K), 0.1087416 secs] [Times: user=0.24sys=0.04, real=0.11 secs]
13.098: [GC (CMS Initial Mark) [1 CMS-initial-mark: 897364K(1756416K)]936667K(2063104K), 0.0041705 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
13.102: [CMS-concurrent-mark-start]
13.341: [CMS-concurrent-mark: 0.238/0.238 secs] [Times: user=0.36 sys=0.01,real=0.24 secs]
13.341: [CMS-concurrent-preclean-start]
13.350: [CMS-concurrent-preclean: 0.009/0.009 secs] [Times: user=0.03 sys=0.00,real=0.01 secs]
13.350: [CMS-concurrent-abortable-preclean-start]
13.878: [GC (Allocation Failure) [ParNew: 16844397K->85085K(18874368K),0.0960456 secs]116885867K->100127390K(155189248K), 0.0961542 secs] [Times: user=0.14 sys=0.00,real=0.05 secs]
14.366: [CMS-concurrent-abortable-preclean: 0.917/1.016 secs] [Times: user=2.22sys=0.07, real=1.01 secs]
14.366: [GC (CMS Final Remark) [YG occupancy: 182593 K (306688 K)]14.366:[Rescan (parallel) , 0.0291598 secs]14.395: [weak refs processing, 0.0000232secs]14.395: [class unloading, 0.0117661 secs]14.407: [scrub symbol table,0.0015323 secs]14.409: [scrub string table, 0.0003221 secs][1 CMS-remark:976591K(1756416K)] 1159184K(2063104K), 0.0462010 secs] [Times: user=0.14sys=0.00, real=0.05 secs]
14.412: [CMS-concurrent-sweep-start]
14.633: [CMS-concurrent-sweep: 0.221/0.221 secs] [Times: user=0.37 sys=0.00,real=0.22 secs]
14.633: [CMS-concurrent-reset-start]
14.636: [CMS-concurrent-reset: 0.002/0.002 secs] [Times: user=0.00 sys=0.00,real=0.00 secs]
~~~ # ParNew: 16844397K->85085K(18874368K), 0.0960456 secs
~~~ 其中, 16844397K 表示GC前的新生代占用量, 85085K 表示GC后的新生代占用量,
~~~ GC后Eden和一个Survivor为空,所以85085K 也是另一个Survivor的占用量。
~~~ 括号中的18874368K 是Eden+一个被占用Survivor的总和(18g)。
~~~ # 116885867K->100127390K(155189248K), 0.0961542 secs
~~~ 其中,分别是Java堆在垃圾回收前后的大小,和Java堆大小。
~~~ 说明堆使用为116885867K=111.47g,回收大小为100127390K=95.49g,
~~~ 堆大小为155189248K=148g(去掉其中一个Survivor),回收了16g空间.
### --- 总结:
~~~ 在HDFS Namenode内存中的对象大都是文件,目录和blocks,
~~~ 这些数据只要不被程序或者数据的拥有者人为的删除,
~~~ 就会在Namenode的运 行生命期内一直存在,所以这些对象通常是存在在old区中,
~~~ 所以,如果整个hdfs文件和目录数多,blocks数也多,内存数据也会很大,
~~~ 如何降低Full GC的影响?
六、计算NN所需的内存大小,合理配置JVM
### --- 使用低卡顿G1收集器
~~~ # 为什么会有G1呢?
~~~ 因为并发、并行和CMS垃圾收集器都有2个共同的问题:
~~~ 老年代收集器大部分操作都必须扫描整个老年代空间(标记,清除和压缩)。
~~~ 这就导致了GC随着Java堆空间而线性增加或减少
~~~ 年轻代和老年代是独立的连续内存块,所以要先决定年轻代和年老代放在虚拟地址空间的位置
~~~ G1垃圾收集器利用分而治之的思想将堆进行分区,划分为一个个的区域。
~~~ G1垃圾收集器将堆拆成一系列的分区,这样的话,大部分的垃圾收集操作就只在一个分区内执行,
~~~ 从而避免很多GC操作在整个Java堆或者整个年轻代进行。
### --- 编辑hadoop-env.sh
~~~ 注意:如果现在采用的垃圾收集器没有问题,就不要选择G1,如果追求低停顿,可以尝试使用G1
[root@linux122 ~]# vim hadoop-env.sh
export HADOOP_NAMENODE_OPTS="-server -Xmx220G -Xms200G -XX:+UseG1GC -
XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions -
XX:+ParallelRefProcEnabled -XX:-ResizePLAB -XX:+PerfDisableSharedMem -
XX:-OmitStackTraceInFastThrow -XX:G1NewSizePercent=2 -XX:ParallelGCThreads=23 -
XX:InitiatingHeapOccupancyPercent=40 -XX:G1HeapRegionSize=32M -
XX:G1HeapWastePercent=10 -XX:G1MixedGCCountTarget=16 -verbose:gc -
XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -
XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=100M -
Xloggc:/var/log/hbase/gc.log -Dhadoop.security.logger=${HADOOP_SECURITY_LOGGER:-INFO,RFAS} -Dhdfs.audit.logger=${HDFS_AUDIT_LOGGER:-INFO,NullAppender}$HADOOP_NAMENODE_OPTS"
Walter Savage Landor:strove with none,for none was worth my strife.Nature I loved and, next to Nature, Art:I warm’d both hands before the fire of life.It sinks, and I am ready to depart
——W.S.Landor
版权声明:本文为yanqi_vip原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。