科技行者

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

知识库

知识库 安全导航

至顶网软件频道通过JRockit提高Linux Java性能分析 (2)

通过JRockit提高Linux Java性能分析 (2)

  • 扫一扫
    分享文章到微信

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

通过分析Java线程堆解决Java应用程序中存在问题的技术。我们可以使用线程堆来分析诸如应用程序挂起,响应时间长以及程序崩溃等情况。在详细介绍分析线程堆的技术之前,我们先来简要地看看线程堆本身。

作者:ChinaITLab 来源:ChinaITLab 2007年10月16日

关键字: 操作系统 java Linux JRockit

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

死锁应用程序中的线程堆

在清单2(只显示了部分线程堆)中,您可以看到JVM已经发现了死锁并且在线程堆中给出了该信息。消息中很清楚地表明了“ExecuteThread: '47' for queue: 'default'”正在等待给被"ExecuteThread: '57' for queue: 'default'"锁住的对象加锁。(这个线程堆来自于WebLogic Server。这些线程都是WebLogic Server的工作线程,它们负责处理客户机请求。)同时,执行线程47也锁住了执行线程57正在等待加锁的一个对象。这就是死锁。在这种状态下,这两个线程都无法继续运行,它们都在等待另一个线程释放锁。

在清单2中,执行线程1正在等待给ConnectionScavanger对象加锁,但是这个对象目前已经被线程47锁住了,正如前面提到过的,线程47又被线程57死锁了。因此,执行线程1也无法继续运行下去。这最终将导致程序的挂起,因为服务器中的其他执行线程随后也会试图获得那个被执行线程47和57锁住的对象的锁。

有些JVM不会给出死锁信息。它只会给出线程堆。这时候,我们可以通过查看线程的状态和堆栈跟踪得出同样的结论。

上述这个情况中的问题源于WebLogic中的一个bug,后来已经修复了。

其他挂起的情况

在一些情况下,您可能会看到大多数线程都是“可运行”的,但是服务器仍然会挂起,无法响应客户机的请求。如果某些线程在做IO(输入输出)操作,它们可能会在执行read()方法的时候被阻塞住,但是状态还是“可运行的”(数据库或其他网络响应的情况可能很糟)。如果出现这种情况,您需要检查数据库和网络是否正常。

一个“可运行的”线程被阻塞住还可能是由下列原因引起的:

· 程序代码中的无限循环。这会导致严重的CPU占用。

· JVM的垃圾收集可能会运行很长时间,占据大量的CPU时间。

· JVM进程可能会用完文件描述符。

如果应用程序的代码调用了wait()方法或sleep()方法,线程在休眠时间内不会继续运行——这时线程的状态就是“在监视中等待”。出现这种情况,应该检查程序代码。

对一个对象的争用也会引起系统性能的下降,最终会导致出现类似于挂起的状态。这种情况下,大多数线程的状态就是“等待监视实体”。所以应该仔细检查程序代码以减少对特定对象的争用。线程可能在等待来自另一个服务器的响应,而另一个服务器可能由于其他原因也处于挂起状态,它就无法给这个服务器发送响应信息。

在上述所有情况中,您都需要每隔几秒取一个线程堆(总共取多个线程堆),从而能够比较所有线程堆中的某一个线程的状态,并且您可以决定线程可以继续运行还是继续等待。

JVM崩溃的情况

JVM的崩溃可能会由下列原因引起:

1.JVM库中的bug。

2.应用程序错误地使用了JNI API。

3.应用程序(原生数据库驱动等)所用的原生模块中存在bug。

通常,当JVM崩溃的时候,它会给出引起崩溃的Java线程的堆栈跟踪。一些JVM会在退出之前给出一个完整的线程堆(所有线程的堆栈跟踪)。我们关心这些线程堆的目的主要在于寻找崩溃时正在运行的是哪个线程。这个线程称为“当前线程”。如果JVM给出一个完整的线程堆,它会标出“当前线程”。因此,一般来说找出引起崩溃的线程还是比较容易的。

注意,崩溃的线程堆通常输出到stdout或stderr。JVM可能也会生成一个二进制格式的核心文件。

崩溃线程堆的例子

如果您检查一下清单3中的崩溃线程堆,您可以看到引起崩溃的线程的堆栈跟踪如下所示:

  
  Current Java thread:
  at com.aaa.bbb.qqq.Direct.setString(Native Method)
  at com.aaa.bbb.qqq.PreparedStatement.setString(PreparedStatement.java:51)
  - locked (a com.aaa.bbb.qqq.PreparedStatement)

清单3
  

最后一个执行的方法(setString)是类“com.aaa.bbb.qqq.Direct”中的一个“原生方法”。因此,我们可以推测出崩溃正是由setString方法的实现中的原生代码引起的。崩溃的原因也可以归结为实现JNI API的JVM库代码有问题。

您还可以看到JVM在堆中给出了下面的信息:

  
  Unexpected Signal : 11 occurred at PC=0x403AC9D1
  Function=(null)+0x403AC9D1
  Library=/opt/sunjdk/1.4.1_01-b01/jre/lib/i386/client/libjvm.so
  # HotSpot Virtual Machine Error : 11
  # Error ID : 4F530E43505002E6
  # Please report this error at
  # http://java.sun.com/cgi-bin/bugreport.cgi
  

这意味着引起崩溃(PC=0x403AC9D1)的代码是JVM库的一部分:"/opt/sunjdk/1.4.1_01-b01/jre/lib/i386/client/libjvm.so"。但这并不表示JVM一定有bug。尽管引起崩溃的指示是在JVM代码中,但崩溃也可能是由于应用程序代码(这里是指原生方法setString的实现)的JNI调用引起的,应用程序代码可能传递了一些错误的变量,从而导致JVM模块的崩溃。

如果在堆中没有“当前线程”的信息,那么我们就需要分析核心堆。这一内容不在本文的讨论范围之中。

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

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

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