JVM

2017/7/30 posted in  java

JVM内存配置

异常的FullGC引发的故障

系统中的portal模块近期频繁出现cpu使用率达到100%的情况,使用top命令查看,发现确实是java业务进程占用异常,重启后可以恢复正常,但一到两天后再次出现。

通过jstat命令查看GC情况时发现是因为老生代被占满,导致进程一直在进行FullGC,且FullGC后老生代仍然使用率为100%,现象如下:

出现这种情况一般有两个可能:

  • 内存溢出。
  • 老生代容量太小,确实有数据存不下。

由于重启后可以保持一段时间正常,且老生代增长并不是特别快,故排除可能内存溢出的可能性,我们准备通过调整jvm内存来修复。

调整虚拟机内存大小

在使用容器时可以调整它的分配内存大小以获取更强的性能,具体操作方法是修改 bin/catalina.sh 文件中的 JAVA_OPTS 配置。

具体如下

#JAVA_OPTS="$JAVA_OPTS -Dorg.apache.catalina.security.SecurityListener.UMASK=`umask`"
JAVA_OPTS="-Xms512m -Xmx1536m -XX:PermSize=256m -XX:MaxPermSize=512m -Xmn512m"

修改文件前请务必备份

各字段含义如下

  • -Xms 初始内存容量
  • -Xmx 最大内存容量
  • -XX:PermSize 老生代初始容量
  • -XX:MaxPermSize 老生代最大容量
  • -Xmn 新生代容量

修改后内容为:

JAVA_OPTS="-Xms1536m -Xmx1536m -XX:PermSize=256m -XX:MaxPermSize=512m -Xmn512m"

正常FullGC记录

下面是一次正常的FullGC记录,Old区从99.91%回收至56.53%, FullGC时间为435ms, YangGC平均时间为15ms

  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT 
  0.00   9.62   9.90  99.91  29.16    443    6.892     0    0.000    6.892
  0.00   9.62  13.23  99.91  29.16    443    6.892     0    0.000    6.892
  0.00   9.62  14.02  99.91  29.16    443    6.892     0    0.000    6.892
  0.00   9.62  36.24  99.91  29.16    443    6.892     0    0.000    6.892
  0.00   9.62  50.07  99.91  29.16    443    6.892     0    0.000    6.892
  0.00   9.62  51.67  99.91  29.16    443    6.892     0    0.000    6.892
  0.00   9.62  51.79  99.91  29.16    443    6.892     0    0.000    6.892
  0.00   9.62  77.47  99.91  29.16    443    6.892     0    0.000    6.892
  8.14   0.00   0.00  99.96  29.16    444    6.906     1    0.000    6.906
  0.00   0.00  10.75  56.53  29.16    444    6.906     1    0.435    7.341
  0.00   0.00  13.19  56.53  29.16    444    6.906     1    0.435    7.341
  0.00   0.00  16.94  56.53  29.16    444    6.906     1    0.435    7.341

深入学习虚拟机内存分区

虚拟机内存分区说明

分区情况如下图所示

其中:

  • Young Generation 存储最新的对象
  • Old Generation 存储老对象,经过多次回收都释放不掉的数据(官方叫法是tenured generation)
  • Permanent Generation 存储虚拟机需要用到的class数据
  • Code Cache 存储代码信息

查看当前各区占用内存的大小

使用jmap命令可以查看虚拟机中的各区内存使用量。具体方法是:

wap@iZ2590mt68kZ:~/app/jdk1.7.0_79/bin$ jmap -heap 32429

32429 是进程的pid
jmap 在java_home/bin目录下

运行结果如下:

wap@iZ2590mt68kZ:~/app/jdk1.7.0_79/bin$ jmap -heap 32429
Attaching to process ID 32429, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.79-b02

using thread-local object allocation.
Mark Sweep Compact GC

Heap Configuration:
   MinHeapFreeRatio = 40
   MaxHeapFreeRatio = 70
   MaxHeapSize      = 1610612736 (1536.0MB)
   NewSize          = 1310720 (1.25MB)
   MaxNewSize       = 17592186044415 MB
   OldSize          = 5439488 (5.1875MB)
   NewRatio         = 2
   SurvivorRatio    = 8
   PermSize         = 268435456 (256.0MB)
   MaxPermSize      = 536870912 (512.0MB)
   G1HeapRegionSize = 0 (0.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 161153024 (153.6875MB)
   used     = 29450688 (28.08636474609375MB)
   free     = 131702336 (125.60113525390625MB)
   18.274983161346075% used
Eden Space:
   capacity = 143261696 (136.625MB)
   used     = 28062800 (26.762771606445312MB)
   free     = 115198896 (109.86222839355469MB)
   19.58848790956656% used
From Space:
   capacity = 17891328 (17.0625MB)
   used     = 1387888 (1.3235931396484375MB)
   free     = 16503440 (15.738906860351562MB)
   7.757322430173993% used
To Space:
   capacity = 17891328 (17.0625MB)
   used     = 0 (0.0MB)
   free     = 17891328 (17.0625MB)
   0.0% used
tenured generation:
   capacity = 357957632 (341.375MB)
   used     = 211810616 (201.99834442138672MB)
   free     = 146147016 (139.37665557861328MB)
   59.17197932519567% used
Perm Generation:
   capacity = 268435456 (256.0MB)
   used     = 78553416 (74.91437530517578MB)
   free     = 189882040 (181.08562469482422MB)
   29.26342785358429% used

24959 interned Strings occupying 3043064 bytes.

这份数据详细展示了当前各区的配置大小和使用率,可以根据它来调整我们的配置。比如:

  • 从结果可以看到我们Perm Generate才使用29%,而class相关的数据应该是比较稳定,所以在分配上存在一定的浪费。我们可以优化配置,让出更多的内存给业务侧。

    优化后,配置为:

    JAVA_OPTS="-Xms1536m -Xmx1536m -XX:NewSize=128m -XX:MaxNewSize=768m  -XX:PermSize=128m -XX:MaxPermSize=128m" 
    

    我在使用jmap时出现了Can't attach to the process 错误,经过查证,可以通过修改系统配置的方式解决,具体方法参见参考链接:jmap报can not attach to the process 问题处理

后期优化记录

由于我们系统内存只有2G,加上一些其它应用占用的内存,采用上述配置会出现内存被占满的情况,经过计算,将系统内存调整为:

JAVA_OPTS="-Xms1024m -Xmx1024m -XX:NewSize=128m -XX:MaxNewSize=768m  -XX:PermSize=128m -XX:MaxPermSize=128m "

参考文献

参考链接:jvmhost.com jvm内存设置

参考链接:jsconsole查看JVM运行状态

参考链接:设置远程监听查看虚拟机运行状态

参考链接:使用jstat查看当前jvm内存信息

参考链接:使用jmap查看内存信息

参考链接:jmap报can not attach to the process 问题处理