Java Performance》作者 Charlie Hunt / Binu John 介绍
Charlie Hunt 现任Salesforce公司的性能工程架构师。曾任Oracle公司首席JVM性能工程师,负责HotSpot Java虚拟机和Java SE类库性能的改进。Charlie拥有美国伊利诺伊理工大学的计算机科学硕士学位、爱荷华州立大学的计算机科学学士学位。
Binu John 是世界上最大的社交网站创建平台Ning.com的高级性能工程师。他目前的职责是着力改善Ning平台的性能和扩展性,以支持每月数百万PV的访问量。Binu拥有美国爱荷华大学生物医学工程和计算机科学硕士学位。
书中jvm推荐配置片段
1、计算存活对象大小(P274)
2、各内存区域配置(P275)
个人总结
1、获取老年代存活对象大小
-
使用 jmap -histo:live pid 获取内存快照,此时会发生Full GC(此方式会影响线上服务的使用,如果使用此方式,可以将改此应用降级后再执行),观察老年代存活对象大小,关于jmap等其他jdk工具更多使用方式可以参考 JDK内置工具使用
-
开启GC日志打印,例配置jvm参数 -XX:+PrintGC 观察多次Full GC后老年代存活对象的大小取平均值为准
2、设置jvm内存区域大小
约定:以 old_size 表示经过Full GC后老年代存活对象大小
- 整个堆大小设置为 old_size 在的 3-4 倍
- 堆中新生代大小为 old_size 的 1-1.5倍
- 堆中老年代大小为 old_size 的 2-3倍,默认新生代与老年代的比例为 1:2 ,不冲突
- 老年代(jdk8为元数据MetaSpace)大小为 old_size 的 1.2-1.5倍
3、配置实践
首先观察一下一个已经运行了33天的vm的gc状况:
在33天内发生了1151次minor gc,每次12323/1151=10.7ms,每天35次左右 ; 6次full gc,每次(12.823-12.323)/6=83.3ms,6天才一次,收集器新生代为默认的CMS,每次发生的原因都是Eden区无法分配内存,综上,内存基本没什么压力低,几天一次的stw几乎对应用无任何影响,我们可以适当的让此jvm活跃起来,减少内存等资源的空闲,把cpu充分的利用起来,再来看一下内存使用的情况:
在未强制执行Full gc前:
可以获得总的堆内存大小为:MaxHeapSize=946M,新生代与老年代最大分别为9461/3=315M、9462/3=630M,此时老年代使用大小为118M
在强制full gc后:
老年代存活对象大小为:91M,那么整个堆设置大小在 91 x 4=364M,永久代(元数据)在 91 x 1.5=136M
修改tomcat配置如下:
修改后堆内存分配,没有意外
查询gc情况
发现在应用启动阶段会发生多次minor gc与少量full GC,我想原因也比较明显,在应用启动阶段,需要创建很多对象,这些对象在新生代达到一定大小就会伴随着minor gc的发生,full gc也如此...
4、更多
如何设置各区域内存,除了此推荐配置外,还需要根据应用特性和硬件环境等其他因素来考虑,例如在32位计算机中,操作系统对进程内存大小是有限制的,设想一个进程限制为2G,那么对应用的堆内存又该如何配置呢?除了划分堆内存,直接内存溢出也会发生OutOfMemoryError。在64位计算机中,单应用如果本身体积就比较大,老年代对象比较多,发生一次Full GC所花费的时间可能会达到几秒甚至几十秒,此时又有什么比较好的解决方式呢?一些简单可行的方法就是设置较大内存来使Full GC尽量少的发生,在流量较少的阶段强制Full GC或重启等,在代码层面我们可以尽量少的创建大对象,使对象在新生代朝生夕死,减少老年代的压力...
关于内存该如何设置?需要根据这些已有经验与应用特性来综合考虑...