环境信息: Solaris 10,Java 1.6.0_30,Jetty 8.1.5,Web应用程序是I / O绑定的,每个进程有1000个大多数空闲线程,6-12个Java进程产生的差别不大,内存16GB未满时失败,CPU容量小于50%,文件描述符设置为65536。
在某些时候使用64位JVM运行时,我们达到了CPU不到50%的状态,机器级和Java进程级别的大量内存仍然可用。 此时,我们开始在产品的各个层获得“IOException”和“EOFException”。据我们所知,目前没有真正的网络或通信问题。似乎Solaris机器耗尽了与套接字通信相关的一些资源,看起来64位JVM消耗的资源是32而不是JVM的两倍。
有什么想法吗? 一个值得注意的区别是64位JVM每个线程堆栈消耗1024k,而32位消耗每个线程仅512k。这可能是原因吗? 是否在Solaris上从同一内存池分配了线程堆栈和套接字I / O? 这个池可以增加吗? 我们是否应该尝试将64位VM上的线程堆栈大小减少到512k?
答案 0 :(得分:0)
原因是堆栈主要包含指针或指针大小的基元。在64位中,它们的大小是32位的两倍。因此,对于相同的堆栈“深度”,您需要加倍字节。 1024K相当于您之前的设置。不要改变它。
答案 1 :(得分:0)
我建议尝试将-XX:+UseCompressedOops
选项(压缩普通对象指针)添加到JVM,看看你的测试如何比较。
此选项可能不是您使用的JVM版本的默认值,可补偿64位指针大小的负面影响,同时保持整体64位虚拟内存空间的优势。
答案 2 :(得分:0)
我的结论是我们遇到的限制如下: (1)服务器处理总内存分配增长超过服务器机器的物理内存的大小 - > (2)Solaris服务器开始将内存页交换到磁盘 - > (3)由于交换,Java垃圾收集变得非常慢(交换非常糟糕,有时单个集合需要超过300秒!) - > (4)当使用套接字通信时,某些进程正在等待I / O,而其他进程则陷入垃圾回收 - > (5)由于执行GC的进程不响应来自其他进程的请求,因此我们在意外的位置获得套接字超时和EOF异常。 (6)我们没有得到OutOfMemory错误,因为从技术上讲,Solaris仍然在其交换文件中有可用内存,并且未超过Java最大堆大小。
因此我的结论如下: (1)32位JVM比64位JVM更好扩展的原因是64位需要50%或更多内存而不是32位,因此64位JVM在32位下分配所有可用物理内存的负载较小。 (2)在给定的Solaris服务器上运行4000个并发会话的关键是减少内存消耗和/或增加可用的物理内存。 (3)为了更好地扩展,你需要32位JVM才能生成更多的线程,默认情况下,它只能生成每个进程给出或接受3000个线程,这对我的特定用例来说还不够。
配置更改: (1)添加以下命令行标志以监视垃圾回收-XX:+ PrintGCDetails -XX:+ PrintGCTimeStamps (2)将线程堆栈大小减小到-Xss384k,这允许每个进程运行更多线程并减少进程的内存分配。 Solaris上32位的默认堆栈大小为512k,64位为1024k。 (3)使用prstat和vmstat命令监视负载测试期间的内存消耗,并确保没有过多的交换。 (4)不要为每个32位Java进程分配超过-mx800m以节省内存并加速垃圾收集,如果需要产生更多进程但确保不填充物理内存。