Optimizeit Thread Debugger概览本文通过介绍Optimizeit Thread Debugger的一些主要特征来使你对它有个简要的了解。如果想要了解更多的信息,请查看Optimizeit Thread Debugger用户手册,也可以从Optimizeit Thread Debugger单击主菜单info|help来查看所有的使用文档。使用中有何问题,请随时与Borland Technical Support联系。测试java程序Optimizeit Thread Debugger是从运行程序的虚拟机中收集程序的覆盖信息的。首先要运行一个java程序,您需要安装一个java虚拟机。Optimizeit Thread Debugger默认安装配置JDK 1.4 or 1.4.1。如果你想使用另外的虚拟机,可以查看Optimizeit Thread Debugger用户手册的如何增加新的虚拟机部分。从运行程序的虚拟机中收集程序的覆盖信息的。首先要运行一个java程序,您需要安装一个java虚拟机。Optimizeit Thread Debugger默认安装配置JDK 1.4 or 1.4.1。如果你想使用另外的虚拟机,可以查看Optimizeit Thread Debugger用户手册的如何增加新的虚拟机部分。启动应用程序Optimizeit Thread Debugger可以测试任何类型的java程序,比如标准应用程序,应用小程序, servlets, JSPs, EJBs。本文所使用的测试程序包含在\doc\thread_debugger\quicktour目录下。所有的测试都是在Optimizeit Thread Debugger中进行的:打开Optimizeit Thread Debugge。如果你是第一次打开,将会自动弹出编辑设置窗口。如果已经打开,可以从file菜单下选择new setting,调出编辑设置窗口。在程序类型框中选择Application。单击"Program main class or Jar file"右面的“Browse…”按钮。找到\doc\thread_debugger\quicktour\ThreadDExample.class文件,然后单击open。Optimizeit Thread Debugger会返回到设置窗口,并且自动带入程序的工作区和类路径。在Source Path框中,单击change…按钮。在Source path chooser窗口中,选择安装路径下的\doc\thread_debugger\quicktour目录;选中后单击窗口中的向下按钮把它加入到source path部分。单击ok增加到源文件中路径中。设置好后的对话框如下:选中Open a console选项;这是非常重要的,因为这个程序需要输入一些命令来运行。 单击Start now按钮。编辑设置窗口会自动关闭, Optimizeit Thread Debugger会启动被测程序。 查看线程Thread Debugger默认打开的是线程监视窗口。你也可以重新定义默认打开的窗口来查找程序在哪里挂起了或者是比较耗时: 程序启动后, Optimizeit Thread Debugger打开了一个DOS窗口,在DOS窗口中输入1,然后按回车键开始第一轮测试。Thread Debugger窗口中会显示当前运行的线程和他们各自的运行状态:整个过程中窗口中会使用不同的颜色条来显示当前的线程运行状态:运行状态 (绿色)表示线程正在运行并使用CPU 。堵塞状态 (黄色)表示该线程没有运行,因为在进入monitor时发生了阻塞。等待状态(红色)表示线程正在等待monitor的通知与其他线程共享资源。等待输入输出状态 (紫色)表示该线程的本地代码没有执行任何的过程。这常常是由于一个输入/输出操作操作引起的,但也有可能是其它情况导致给代码没有分配使用CPU而造成的。注意:当相应的线程状态应该被更新而未更新时,Optimizeit会在颜色条中显示一些点。当测试程序退出或没有收集到信息时,未知状态的区域也将以不同类型的图案来显示。先选中主线程"main",然后单击I/O-Waiting View 切换到等待I/O监视窗口: 双击第三行,可以从源代码窗口中看到发生等待的那行代码。该程序基本上都使用了标准的输入来方法。关闭源码窗口并返回到线程活动监视窗口。在线程窗口中也可以选取某一段来分析该时间内的一些信息。返回到控制窗口并且输入2,然后单击回车启动第二轮测试(使用了小的连接)。Thread Debugger显示了当前正在运行的线程的状态。你会看到在开始的某段时间内有10个线程在运行。 在线程活动监视窗口,拖动滚动条到刚才的这段时间范围内。如果不想继续查看线程活动信息,可以取消选中窗口右下方的Update continuously选项,这样窗口中的信息就不会随时间变化了。 在这段时间内黄色区域表示相应的线程堵塞在一个monitor中。在线程条上直接单击黄色条上任意一部分,并按住鼠标左键向右移动。这样就选中了该时间段: 选中时间段后:单击Contention View 来分析为什么线程在进入monitors时会发生阻塞。该信息表示的仅是选择时间段的。 单击Waiting View 来研究线程在哪里等待monitors。同样,该信息也仅仅是所选时间范围的,所以应选择图中红色条部分的区域。注意:如果线程已经退出或者已不存在该线程名在窗口中就会以斜体显示。理解线程争用在java多线程的应用程序中如果许多线程同时需要同样的monitors(可译为监视器或管程),就会造成严重的性能瓶颈。Monitors是被用来保护共享资源被多个线程同时调用的,每一个对象都有一个monitors,同时只允许一个线程持有monitors从而进行对对象的访问,那些没有获得monitors的线程必须等待,直到持有monitors的线程释放monitors。这部分被monitors保护起来的代码,我们称之为临界区。为了优化性能,我们经常要尽可能地保持小的临界区,特别是当临界区在执行一些不耗用CPU资源的过程时。如果一个线程在临界区域内由于等待输入输出而造成等待,它就不再使用CPU资源。然而,如果其他的线程也等待这个monitors的话就就不能充分利用CPU资源。下面我们用实例来说明这个问题:重新启动被测试程序。程序启动后,在DOS窗口中输入1并按下回车键启动争用较大的示例。你会注意到在某段时间内有10个线程在运行。在线程窗口,拖动窗口下面的滚动条返回到这段时间,并且取消选中"Update continuously": 可以看到此例中相对于实际使用CPU的时间来说争用monitor所花费的时间更长。为了更全面地了解这个情况,选中任何线程(例如线程7)并且单击Contention View 切换到争用查看窗口: 由于争用ContentionExample$Consumer.run()方法100%的时间花在了堵塞上,争用监视控制台上显示了所有与该争用有关的monitor。选中java.lang.Object monitor。争用详细说明控制台就会显示列出所有与该争用有关的线程,包括什么时候发生了争用。打开第一个线程: 在ContentionExample$Consumer.run()行上双击就会弹出该方法的源代码。在源码窗口显示线程7正在获取monitor,然而源码窗口下方显示线程1同时也在获取monitor: 在被monitor“lock”保护的临界部分,此例中调用了方法processData()。该方法用1毫秒来模拟输入输出。此例说明了使用过多的争用会付出很大代价。对于使用几毫秒cpu资源来说, 每个线程大概阻塞1秒。为了降低争用的发生,当确认临界区不再等待争用时,有必要使临界区最小化。我们的解决办法很简单,就是通过排除不需要同步处理的数据来处理缩小临界区。这样代码调整为: synchronized (lock) { a = dataSourceA.nextElement(); b = dataSourceB.nextElement();}processData(a, b);演示程序中也包含了测试useBigLock 标志的情况。切换到线程监视窗口。重启被测程序,在DOS窗口中输入2 后按下回车键运行第二个测试。第二个测试也使用的同样的代码,但是这次useBigLock 标记被置为false。争用时间降低到了每个线程只有几毫秒。 这里有一些技巧来防止由于争用而引起的性能问题:尽量使临界区最小。仅包含需要被保护的语句。锁定可能最低的级别。在synchronized方法前加上explicit synchronized()语句。同步方法使用起来很方便,但是也可能导致过多的争用,仅仅因为随着时间的增长方法趋向于复杂化。 不要在临界区执行一个I/O操作,除非唯一的目的是为了保护I/O描述符或者对象经常执行I/O操作。 猜测争用的级别几乎是不可能的。通过监视线程窗口,每次重大的变化都会看到有难以理解的争用级别。 理解死锁多线程的应用程序所面对的共同问题是,当一些线程由于不能获得其所需的资源致使线程被挂起。死锁对堆栈来说是极大的挑战,因为它经常很少出现甚至不能复现。一个极少出现死锁也有可能导致一个web应用程序长时间被挂起,并引起资源浪费。Optimizeit Thread Debugger提供了两个强有力的特征使调试死锁变得容易。第一是给开发人员提供死锁发生的位置。第二个特征将在下一部分来说明,是帮助开发人员来监视应用程序的运行,并且一旦有可能发生死锁会及时给出提示。单击Monitor View 按钮切换到显示执行临界部区所有的线程和监视器的窗口。默认情况下该窗口的内容是实时显示并每秒都更新的。 如果有必要,重启被测试程序并在DOS窗口种输入3然后回车来运行一个死锁的例子。这个死锁是由于一个同步的参数错误引起的: 单击图中的任一个按钮,相应的栈轨迹就会显示在窗口的下方。可以双击某一行来查看相应的源码。线程颜色所表示的状态与线程窗口中所表示的一样。一旦单击连接图中的一个按钮,监视内容就不再更新。可以再次单击该按钮或者重新进入该窗口图就可以继续保持更新。监视程序的死锁由于重现和理解死锁是比较困难的,基于这种情况, Optimizeit Thread Debugger提供了一个线程分析器。当一个java程序运行时线程分析器记录并监视所有的同步活动。然后搜索任何一个有可能导致死锁的类型并且给出一个警告或者错误信息的列表,包含临界区的详细资料,诸如哪里会有有可能发生死锁和调用了那个线程。Optimizeit Thread Debugger会注意监视以下一些表现异常的锁类型:锁的顺序:两个线程直接或者连续地以不同的顺序进入同一个monitors。锁和等待:线程进入一个monitors然后等待进入其他的monitors。锁和I/O等待:线程进入一个monitors然后停止执行任何的任务。除非两个线程永远不会同时运行,锁的顺序问题经常预示着有被锁死的风险。对Lock-and-wait和Lock-and-I/O-wait类型的锁,预示着情况比较正常,如果监视器的目的是限制对正在等待或者正在执行输入输出的资源的访问。为了证实会发生死锁,本文所指的示例程序中会有3个线程会同时进入同一个监视器来获取数据。每个线程都使用了随机函数调用,这样有时它们会以不同的顺序进入监视器。这个程序仅仅用来演示,然而可以细想当多个线程在一个大的应用程序中运行并且当处理一些临界区时以不同的顺序进入监视器会发生什么样的情况。本例在执行过程中经常会发生死锁,但是无论是否真正地发生死锁Optimizeit Thread Debugger分析器都将汇报一些警告信息和发生的错误信息:如有必要请重新打开示例程序。 单击Monitor Usage Analyzer 按钮。单击Record 开始记录测试过程。在程序启动后会弹出一个DOS窗口,在窗口中输入4并按回车键开始程序的运行。注意:分析器也可以通过选择编辑设置窗口中的"start analyzer"选项来启动。当以一定的顺序发生阻塞时窗口中显示符号 '>',以另外的顺序发生阻塞时显示符号'<'。最后,程序会停止,但是再不会出现菜单。这意味着发生了死锁。按下回车键可以重新显示菜单: 再次单击Record停止记录。选择第一条错误信息并切换到分析窗口: 在方法名上单击调出源代码。关闭源码窗口返回到监视器使用分析窗口。这里有一些避免产生死锁的技巧:尽可能让锁简单。两个线程使用一个monitors来共享资源不会发生死锁,同样他们永远不会在临界区期间发生阻塞。在进入monitors后不要执行输入输出操作,除非该监视器是用于保护输入输出的。当进入另一个monitors时不要等待另一个monitors,除非完全有必要并且是可理解的。总是以同样的顺序进入monitors。当持有一个锁时不要调用公共的synchronized方法。活锁过多在多线程的应用程序中另一类典型的问题是有过多的锁。当虚拟机被优化后,能够非常快地进入多个monitors,减少monitors的使用就可以极大地提高性能。Optimizeit Profiler能够用来度量每个方法使用了多长时间,包括进入monitors的时间,而Optimizeit Thread Debugger可使开发人员了解监视器使用的频率和位置,无论争用是否发生过:在线程窗口选择一个线程或一个时间范围后单击Monitor Enter View 按钮切换到进入monitors详细信息查看窗口。例如,在选择main类后单击出现如下所示的窗口:可以看到main类进入了22个monitor。 本文只是Optimizeit Thread Debugger的一个概览。如果想要了解更多有关Optimizeit的信息,请查看Optimizeit Thread Debugger用户指南。 翻译:
[email protected] 时间:2004-11-11如果您对本文或白盒测试感兴趣,请登录http://groups.yahoo.com/group/WhiteBoxTestCN/,加入会员,即可下载到本文完整的内容(格式为呆图片的word文档),在这里还有其他一些白盒测试工具的介绍与相关讨论。