科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道Java EE 常见性能问题解决手册4

Java EE 常见性能问题解决手册4

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

这篇文章,是PRO JAVA EE 5 Performance Management and Optimization 的一个章节,作者Steven Haines分享了他在调优企业级JAVA应用时所遇到的常见问题。

来源:IT专家网 2008年6月3日

关键字: 问题 3977 Java EE java

  • 评论
  • 分享微博
  • 分享邮件
我第一次碰到这种问题的时候,用户抱怨说程序性能很差劲,并且在运行了几次后就出现了问题,可能是内存溢出问题。在我调查了详细的关于堆和程序内存利用的收集器的记录后,我迅速发觉堆的状态非常正常,但程序确发生了内存溢出。这个用户维持了数千的JSP页面,在装载到内存前把他们都编译成了字节流文件放入持久空间。他的环境已经造成了持久空间溢出,但是在堆中由于用了 -noclassgc 选项,于是JVM并不去释放类文件来装载新的类文件。于是就导致了内存溢出错误,我把他的持久空间改为512M大小,并去掉了 -noclassgc 参数。

  正像图7显示的,当持久空间变满了的时候,就引发垃圾收集,清理了乐园和幸存者空间,但是并不释放持久空间中的一点内存。

  Figure 7. Garbage collection behavior when the permanent space becomes full. Click on thumbnail to view full-sized image.

  注意

  当设置持久空间大小时候,一般考虑128M,除非你的程序有很多的类文件,这个时候,你就可以考虑使用256M大小。如果你想让他能够装载所有的类的时候,就会导致一个典型的结构错误。设置成512M就足够了,它仅仅是暂时的时间的花费。把持久空间设置成512M大小就象给一个脚痛的人吃止痛药,虽然暂时缓解了痛,但是脚还是没有好,依然需要医生把痛治疗好,否则只是把问题延迟了而已。

  线程池

  外界同WEB或程序服务器连接的主要方法就是向他们发送请求,这些请求被放置到程序的执行次序队列中。和内存最大的冲突就是程序服务器所设置的线程池的大小。线程池的大小就是程序可以同时处理的请求的数量。如果池太小,请求就需要在队列中等待程序处理,如果太大,CPU就需要花费太多的时间在这些众多的线程之间来回的切换。

  每个服务器都有一个SOCKET负责监听。程序把接受到的请求放到待执行队列中,然后将这个请求从队列移动到线程中被程序处理。

  图8显示了服务器的处理程序。

  Figure 8. 服务器处理请求的次序结构

线程池太小

  每当我碰到有人抱怨装载速度的性能随着装载的数量的增加变的越来越糟糕的时候,我会首先检查线程池。特别是,我在看到下面这些信息的时候:

  •   1.线程池的使用
  •   2.很多请求等待处理(在队列中等待处理)

  当一个线程池被待处理的请求装满的时候,响应的时间就变的极其糟糕,因为这些在队列中等待处理的请求会消耗很多的额外时间。这个时候,CPU的利用率会非常低,因为程序服务器没有时间去指挥CPU工作。这个时候,我会按一定幅度增加调节池的大小,并在未处理请求的数量减少前一直监视程序的吞吐量,你需要一个合理甚至更好的负载量者,一个精确的负载量测试工具可以准确的帮你测试出结果。当你观测吞吐量的时候,如果你发现吞吐量降低了,你就应该把池的大小下调一个幅度,一直到找到让它保持最大吞吐量的大小为止。

  图9显示了连接池太小的情况

点击放大此图片

  Figure 9. 所有的线程都被占用了,请求就只能在队列中等待

  每当我阅读性能调整手册的时候,最让我头疼的就是他们从来不告诉你特殊情况下线程池应该是多大。由于这些值非常依赖程序的行为,他们只告诉你大普通情况下正确的大小,但是他们给了你一个范围内的值,这对用户很有利的。例如考虑下面2种情况::

  •   1. 一个程序从内存中读出一个字符串,把它传给JSP页面,让JSP页面去显示
  •   2. 另一个程序从数据库中读出1000个数值,为这些不规则的数值求平均。第一个程序对请求的回应会很块,大概仅需要不足0.25秒的时间,且不怎么占据CPU。第二个程序可能需要3秒去回应,同时会占据CPU。因此,为第一个程序配置的池大小是100就有点太小了,因为程序能够同时处理200个;但为第二个程序配置的池是100,就有点太大了,因为CPU可能就能应付50个线程。

  但是,很多程序并没有在这种情况下动态的去调整的功能。多数情况下是做相同的事,但是应该为他们划分范围。因此,我建议你为一个CPU分配50到75个左右的线程。对一些程序来说,这个数量可能太少,对另一个些来说可能太多,我刚开始为每个CPU分配50到75个线程,然后根据吞吐量和CPU的性能,并做适当的调整。

  线程池太大

  除了线程池数量太小之外的情况外,环境也可能把线程数量配置的过大。当这些环境中的负载量不断增大的时候,CPU的使用率会持续无法降低,就没有什么响应请求的时间了,因为CPU只顾的在众多的线程之间来回的切换跳动,没时间让线程去做他们应该做的事了。

  连接池过大的最主要的迹象就是CPU的使用率一直很高。有些时候,垃圾收集也可能导致CPU使用率很高,但是垃圾收集导致的CPU使用率很高和池过大导致的使用率有一个主要的区别就是:垃圾收集引起的只是短时间的高使用率就象个钉子,而池过大导致的就是一直持续很高呈线性。

  这个情况发生的时候,请求会被放在队列中不被处理,但是不会始终如此,因为请求占用CPU的情况和程序占用的情况造成的后果不同。降低线程池的大小可能会让请求等待,但是让请求等待总比为了处理请求而让CPU忙不过来的好。让CPU保持持续的高使用率,同时性能不降低,新请求到来的时候放入到队列中,这是最理想的程序。考虑下面这个很类似的情况:很多高速公里有交通灯来保证车辆进入到拥挤的公里中。在我看来,这些交通灯根本没用,道理很充分。比如你来了,在交通灯后面的安全线上等待进入到高速公路上。如果所有的车辆都同时涌向公里,我们就动弹不得,但是只要减缓涌向高速公路车辆的速度,交通迟早会畅通。事实上,很多的大城市都有这样功能,但根本没用,他们真正需要的是一些更多的小路(CPU),涌向高速公路的速度真的降低了,那么交通会变的正常起来。

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章