天天看点

说说在Java启动参数上易犯的错

作者:林昊

说说在Java启动参数上易犯的错

前几年在将os从32 bit升级到64 bit,以及虚拟机的内存调整到8g后,我把应用的java启动参数重新写了一版,作为目前大部分java应用的默认启动参数模版,这几年下来,发现在这个标准版的启动参数上还是犯了一些错误的。

1. -xx:+disableexplicitgc

java在实现rmi server的时候会通过定时的调system.gc来强制做gc(即使程序里没用到rmi也会被启动),这个动作非常烦人,另外也是为了避免应用代码上显式去调用system.gc导致一些没必要的gc动作产生,所以当时就直接加上了这个参数。

现在来看,这个参数有个挺大的问题是,direct bytebuffer所占用的内存以及filechannel.map所占用的内存当达到了他们的最大阈值时,需要依赖调用system.gc来强制释放下,如果加上了这个启动参数,就意味着这个强制的释放就无效了,这会导致的一个问题是,当old gen还没到达触发full gc/cms gc的条件,而堆外的direct bytebuffer/filechannel.map占用的空间又超过了它们的最大阈值时,就会直接导致oom,而这种情况下很有可能其实是可以借助显式调用system.gc来释放出足够的空间,不过话说我仍然觉得这是jdk设计上应该改进的一点,不应该在这个时候需要依赖system.gc来管理堆外的空间,大家可以翻下filechannel.map的代码就会发现那里在等待system.gc执行的结果是写s的等待100ms,事实上很少有full gc/cms gc可以在100ms完成。

不过鉴于上面的状况,如果应用里有使用到不少direct bytebuffer或filechannel.map的话,建议还是不要开启-xx:+disableexplicitgc,如果是cms gc的,还是改为加上这个参数-xx:+explicitgcinvokesconcurrent,另外如果有rmi server这种定时gc影响的,再调整下-dsun.rmi.dgc.client.gcinterval和-dsun.rmi.dgc.server.gcinterval这两个时间吧,时间单位是ms,也可以设置为long.max_value。

2. 缺少-xx:+usecmsinitiatingoccupancyonly

由于我们的java应用的heap基本都是大于4g的,所以都是用的cms,当时我在写启动参数的时候一直犹豫要不要加上-xx:+usecmsinitiatingoccupancyonly这个参数,一犹豫就没加,但事实上后来碰到了不少应用由于jvm自行触发cms gc的机制导致cms gc频繁,所以建议用cms gc的场景下还是加上这个参数更稳妥。

3. -xx:maxdirectmemorysize

话说在写启动参数的时候我都压根不知道这参数(要知道java到底有哪些启动参数可用,以及默认值是多少,最靠谱的方法是在启动参数上加-xx:+printflagsfinal或用jinfo -flags [pid]来查看),后来是由于有一次出现了有应用物理内存被耗光,排查的时候才发现是direct bytebuffer这块默认的大小是heap size,所以在有些情况下可能会出现direct bytebuffer这里占用了大量的空间,但heap这边又还不到触发full gc/cms gc的条件,就会有可能导致物理内存被耗光。

因此对于远程交互比较多的应用,建议还是加上这个参数,合理控制大小,不要让heap size+direct memory size就把物理内存给耗光了。

ps: 关于java常见问题的排查方法,重新专门写了一个ppt,涵盖了以下几种常见的java问题的排查方法:

1. 类加载问题,例如nosuchmethodexception;

2. 内存问题,例如各种oom;

3. 应用无响应问题,例如http访问后返回499;

4. cpu利用率问题,例如us耗尽;

5. java进程退出问题。