一、分配大容量的堆内存,可能导致一次Full GC的停顿时间过长(网站不定期失去响应)。
给虚拟机分配超大堆的前提:
是有把握把应用程序的Full GC频率控制得足够低,至少要低到不影响用户使用。 譬如十几个小时乃至一天才会出现一次Full GC, 这样可以通过在深夜执行定时任务的方式触发Full GC甚至自动重启应用服务器来保持内存的可用空间在一个稳定的水平。
控制Full GC频率的关键:
应用中绝大多数对象符合“照生夕灭”的原则,即大多数对象的生存时间不应太长,尤其不能有成批量的、长生存时间的大对象产生,这样才能保障老年代空间的稳定。
二、堆外内存导致的溢出错误
如果程序不定时抛出内存溢出错误,但jstat(上一篇讲到的,虚拟机统计信息监视工具)显示,GC并不频繁,Eden区、Survivor区、老年代内存全部正常,压力不大。
这时候可以考虑,是否是堆外内存不够,导致的溢出错误。
堆外内存:
- Direct Memory
可通过-XX:MaxDirectMemorySize调整大小,内存不足时抛出OutOfMemoryError或者OutOfMemoryError: Direct buffer memory. - 线程对战:
可以通过-Xss调整大小,内存不足时抛出StackOverflowError或者OutOfMemoryError: unable to create new native thread. - Socket缓存区:
每个Socket连接都有Receive和Send两个缓冲区,分别占37KB和25KB内存,如果连接多,这块内存占用也很可观。如果无法分配,可能会抛出IOException: Too manay open files异常 - JNI代码:
如果代码中使用JNI调用本地库,那本地库使用的内存也不在堆中。 - 虚拟机和GC:
虚拟机、GC的代码执行也要消耗一定的内存。
[1] 周志明 · 深入理解Java虚拟机 :机械工业出版社