性能优化-JVM内存分配
5.11 JVM内存分配
JVM内存分配的原则遵循如下顺序
- 先确定一个合适的总大小,以避免过小内存造成频繁的全局垃圾回收,对Java系统产生的致命影响;过大分配对操作系统的其他进程使用造成影响从而降低系统运行性能。内存大小分配JVM最主要去优化的方式。
- 微调JVM内存参数,比如年轻代和老年代比例以提高系统吞吐量,大对象设定阈值使得大对象直接在老年代分配等,选择垃圾回收算法等,微调需要长期观察系统功能运行,需要较多的JVM背景知识,将在第10章《JVM可观测和调优》中说明。从个人经验来看,微调大约最多能增加系统5%的吞吐量。
Java系统运行需要操作系统其配置内存大小,比如设置大堆大小为2G,即最小堆和最大堆均为2G。
-Xmx2g -Xms2g当Java系统运行,JVM会向操作系统申请2G,操作系统根据实际使用分配。调用操作系统命令top查看进程的内存使用情况,可以看到Res参数表示进程实际使用内存空间,包括堆内和对外合计,占用未到4G空间
$top
top - 18:38:51 up 286 days, 4:01, 2 users, load average: 0.44, 0.39, 0.44
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
924787 tsoud-uu 20 0 7887296 1.5g 12704 S 7.7 2.4 1779:18 java即使RES显示超过2G,这也并不意味着该Java进程内存占用了2G而需要扩大。实际上JVM申请的空间只有在进程实际用到的时候才会被操作系统分配。当Java进程用垃圾回收的时候,会清理未使用对象从而有充足的堆内存,尽管此时从操作系统看,已经在使用了2G大小。
确定JVM内存大小分配是否合理,需要查看系统长期运行下的垃圾回收频率,运行JDK自带的命令jstat,
jstat –gcutil -h 20 pid 1000 100此命令显示进程为pid的内存使用汇总,1000毫秒输出一次,总共输出100行。 -h 20 表示每20行输出一次表头。-gcutil表示显示JVM内存使用汇总统计
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
71.15 0.00 51.52 0.01 90.07 78.24 6 0.016 0 0.000 0.016
71.15 0.00 61.18 0.01 90.07 78.24 6 0.016 0 0.000 0.016
71.15 0.00 73.16 0.01 90.07 78.24 6 0.016 0 0.000 0.016
71.15 0.00 82.82 0.01 90.07 78.24 6 0.016 0 0.000 0.016
71.15 0.00 93.73 0.01 90.07 78.24 6 0.016 0 0.000 0.016
0.00 25.53 5.01 0.18 90.07 78.24 7 0.019 0 0.000 0.019
0.00 25.53 15.85 0.18 90.07 78.24 7 0.019 0 0.000 0.019此命令输出内容较多,目前只需要观察FGC列,它是用来衡量Java进程的内存设置是否合理的重要标识。FGC代表从运行此命令后,全局垃圾回收的次数,图上显示0次,我们可以在生产上运行此命令数小时观察结果(执行此命令对目标系统无性能影响)
一般来说,一个正常运行的业务系统,FGC可能1天才数次,或者数天才1次。即使业务高峰期,有可能是数小时1次的情况。如果同时满足以下情况意味着需要设置较大堆内存
- 业务请求并没有明显变化
- FGC 数分钟出现一次,或者更短时间出现一次,这代表老年代内存(上图输出的
O列)不够用。即堆内存设置过小
这时候需要重新设置堆大小,比如改成
-Xmx4g -Xms4g这一节未涉及到JVM全部知识,以及未涉及到如何长期观测JVM运行状态,将会在本书10章讲解。但需要强调,堆大小是JVM内存设置首要考虑的参数。普通无状态的业务系统,初始化一个2G或者4G大小即可。
