0

伙计们,上周在一个大型客户站点的 Java 服务器应用程序的负载测试研讨会期间,我们在使用 64 位 JVM 时只能运行大约 1300-1800 个并发会话,而在相同的配置下,我们能够运行 2800 个并发会话使用 32 位 JVM。

环境信息: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 位 JVM 每个线程仅消耗 512k。这可能是原因吗?线程堆栈和套接字 I/O 是否从 Solaris 上的同一个内存池分配?这个池子可以增加吗?我们应该尝试将 64 位 VM 上的线程堆栈大小减少到 512k 吗?

4

3 回答 3

0

我的结论是,我们遇到的限制如下:(1)服务器进程的总内存分配增长超过了服务器机器的物理内存大小-->(2)Solaris服务器开始交换内存页面到磁盘- -> (3) 由于交换,Java 垃圾收集变得非常慢(交换非常糟糕,有时一次收集需要超过 300 秒!)--> (4) 使用套接字通信时,一些进程正在等待为 I/O 而其他人陷入垃圾回收 --> (5) 我们在意想不到的地方得到套接字超时和 EOF 异常,因为执行 GC 的进程不响应来自其他进程的请求。(6) 我们没有遇到 OutOfMemory 错误,因为从技术上讲,Solaris 的交换文件中仍有可用内存,并且尚未超过 Java 最大堆大小。

因此我的结论如下: (1) 32 位 JVM 比 64 位 JVM 扩展性更好的原因是 64 位比 32 位需要 50% 或更多的内存,因此 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 以节省内存并加快垃圾收集速度,如有必要,请生成更多进程,但请确保不要填满物理内存。

于 2013-09-24T08:25:19.857 回答
0

我建议尝试将-XX:+UseCompressedOops选项(压缩普通对象指针)添加到 JVM 并查看您的测试比较。

此选项可能还不是您使用的 JVM 版本的默认选项,它补偿了 64 位指针大小的负面影响,同时保持了 64 位虚拟内存空间的整体优势。

于 2013-09-01T09:10:12.600 回答
0

原因是堆栈主要包含指针或指针大小的原语。在 64 位中,它们的大小是 32 位中的两倍。因此,对于相同的堆栈“深度”,您需要双倍字节。1024K 相当于您之前的设置。不要改变它。

于 2013-09-01T07:50:43.840 回答