影响Java EE性能的十大问题

Posted on

影响Java EE性能的十大问题

本文作者是一名有10多年经验的高级系统架构师,他的主要专业领域是Java EE、中间件和JVM技术。他在性能优化和提升方面也有很深刻的见解,下面他将和大家分享一下常见的10个影响Java EE性能问题。

1.缺乏正确的容量规划

容量规划是一个全面的和发展的过程标准,预测当前和未来的IT环境容量需求。制定合理的容量规划不仅会确保和跟踪当前IT生产能力和稳定性,同时也会确保新项目以最小的风险部署到现有的生产环境中。硬件、中间件、JVM、调整等在项目部署之前就应该准备好。

2.Java EE中间件环境规范不足

“没有规矩,不成方圆”。第二个比较普遍的原因是Java EE中间件或者基础架构不规范。在项目初始,新平台上面没有制定合理的规范,导致系统稳定性差。这会增加客户成本,所以花时间去制定合理的Java EE中间件环境规范是必须的。这项工作应与初始容量规划迭代相结合。

3.Java虚拟机垃圾回收过度

各位对“java.lang.OutOfMemoryError”这个错误信息是不是很熟悉呢?由于JVM的内存空间过度消耗(Java堆、本机堆等)而抛出的异常。

垃圾收集问题并不一定会表现为一个OOM条件,过度的垃圾收集可以理解成是JVM GC线程在短时间里进行轻微或超量收集集合数据而导致的JVM暂停时间很长和性能下降。可能有以下几个原因:

1、与JVM的负载量和应用程序内存占用量相比,Java堆可能选择的太小。

2、JVM GC策略使用不合理。

3、应用程序静态或动态内存占用量太大,不适合在32位JVM上使用。

4、JVM OldGen随着时间推移,泄漏越来越严重,而GC在几个小时或者几天后才发现。

5、JVM PermGen空间(只有HotSpot VM)或本机堆随着时间推移会泄露是一个非常普遍的问题;OOM的错误往往是观察一段时间后,应用程序进行动态调动。

6、YoungGen和OldGen的比例空间与你的应用程序不匹配。

7、Java堆在32位的VM上太大,导致本机堆溢出,具体可以表现为OOM试着去链接一个新的Java EE应用程序、创建一个新的Java线程或者需要计算本地内存分配任务。

建议:

1、观察和深入理解JVM垃圾回收。启动GC,根据健康合理的评估来提供所有的数据。

2、记住,GC方面的相关问题不会在开发中或者功能测试时发现,它需要在多用户高负载的测试环境下发现。

4.与外部系统集成过多或过少

导致Java EE性能差的第四个原因是高分布式系统,典型案例是电信IT环境。在这个环境中,一个中间件领域(例如,服务总线)很少会做所有的工作,而仅仅是把一些业务“委托”给其他部分,例如产品质量,客户资料和订单管理,到其他Java EE中间件平台或遗留系统中,如支持各种不同的负载类型和通信协议的大型机。

这样的外部系统调用意味着客户端的Java EE应用程序触发创建或重用套接字链接从外部系统中读写数据。根据业务流程的实施和实现可以配置成同步调用或异步调用。需要注意的是,响应时间会根据外部系统的稳定状况进行改变,所以通过适当的使用超时来保护Java EE应用程序和中间件也是非常重要的。

下面这3种情况是经常出现问题和性能降低的地方:

1、同步和相继调用太多的外部系统。

2、在Java EE客户端应用程序和外部系统之间链接超时,使数据丢失或者值太高导致客户端线程被卡住,从而导致多米拉效应。

3、超时,但程序仍正常执行,可是中间件不处理这种奇怪的路径。

最后,建议多进行负面测试,这意味着需要“人为”创造产生这些问题的条件,用来测试应用程序和中间件之间是如何处理外部系统错误。

5.缺乏适当的数据库SQL调优和容量规划

大家可能会对这一个感到惊奇:数据库问题。大多数Java EE企业系统是依赖关系型数据库处理复杂的业务流程。一个基础扎实稳固的数据库环境可以确保IT环境有规模的增长,来支持日益不断扩大的业务。

在实际中,与数据库相关的性能问题是很常见的。由于多数数据库事务处理都是由JDBC数据源执行的(包括关系持久化API,例如Hibernate)。而性能问题最初都会表现为线程阻塞。

以下是我在10年的工作中,经常出现的关于数据库方面的问题(以Oracle数据库为例):

1、孤立的,长时间运行的SQL。主要表现为线程阻塞、SQL没有进行优化、缺少索引、非最佳的执行计划、返回大量数据集等等。

2、表或行级数据锁定。当提交一个双阶段事务模型时(例如,臭名昭著的Oracle可疑事务)。Java EE容器可能会留下一些未处理的事务等待最后的提交或回滚,留下的数据锁能触发性能问题,直到最后的锁被移除。例如中间件断电或者服务器崩溃都可能引起这些情况发生。

3、缺乏合理规范的数据库管理工具。例如Oracle里面的REDO logs,数据库数据文件等。磁盘空间不足,日志文件不旋转等都会触发较大的性能问题和断电情况。

建议:

1、合理的容量规划,包括负载和性能测试都是必不可少的,优化数据环境和及时发现问题。

2、如果是使用Oracle数据库,确保DBA团队定期审查AWR报告,尤其是在上下关联的事件和根源分析过程中。

3、使用JVM线程存储和AWR报告查明SQL运行缓慢的原因或者使用监控工具来做。

4、加强“操作”方面的数据库环境(磁盘空间、数据文件、重做日志、表空间等)以适当的监视和报警。如果不这么做,会让客户端IT环境出现较多的断电情况和花许多时间进行故障调修。

6.特定应用程序性能问题

下面关注的是比较严重的Java EE应用程序问题。关于特定应用程序性能问题,总结了以下几个点:

1、线程安全的代码问题

2、通信API缺少超时设置

3、I/O、JDBC或者关系型API资源管理问题

4、缺乏适当的数据缓存

5、数据缓存过度

6、过多的日志记录

7.Java EE中间件调优问题

一般Java EE中间件都已经够用了,只是缺少必要的优化。大多数Java EE容器都能有多种方案供你的应用程序和业务进程选择。

如果没有进行适当的调整和实践,那么Java EE容器可能会处于一种消极的状态。

下图是视图和检查列表示例:

8.主动监控不足

缺乏监控,并不会带来实际性能问题,但它会影响你对Java EE平台性能和健康状况的了解。最终,这个环境可以达到一个破发点,这可能会暴露出一些缺陷和问题(JVM的内存泄漏,等等)。

以我的经验来看,如果一开始不进行监控,而是运行几个月或者几年后再进行,平台稳定性将大打折扣。

也就是说,改善现有的环境永远都不会晚。下面是一些建议:

1、复查现有Java EE环境监测能力和找到需改进的地方。

2、监测方案应该尽可能的覆盖整个环境。

3、监控方案应该符合容量规划进程。

9.公共基础设施硬件饱和

这个问题经常在有太多的Java EE中间件环境随着JVM进程被部署到现有硬件上面时看到。太多的JVM进程对有限的物理CPU核心来说是一个真正的程序性能杀手。另外,随着客户端业务的增长,硬件方面也需要再次考虑。

10.网络延迟

最后一个影响性能问题的是网络,网络问题时不时的都会发生,如路由器、交换机和DNS服务器失败。更常见的是在一个高度分散的IT环境中定期或间歇性延迟。下面图片中的例子是一个位于同一区域的Weblogic集群通信与Oracle数据库服务器之间的延迟。

间歇或定期的延迟会触发一些重要的性能问题,以不同的方式影响Java EE应用程序。

1、因为大量的fetch迭代(网络传入和传出),涉及大数据集的数据查询问题的应用会非常受网络延迟的影响

2、应用程序在处理外部系统大数据负载(例如XML数据)时也会很受网络延迟的影响,会在发送和接收响应时产生巨大的响应间隔。

3、Java EE容器复制过程(集群)也会受到影响,并且会让故障转移功能(如多播或单播数据包损失)处于风险中。

JDBC行数据“预取”、XML数据压缩和数据缓存可以减少网络延迟。在设计一个新的网络拓扑时,应该仔细检查这种网络延迟问题。

希望本文能够帮助您理解一些常见的性能问题和压力点,每个IT环境都是独一无二的,所以文中提到的问题不一定会是您遇到的,您可以把您遇到的问题拿出来和大家一起分享一下!

离线分析java内存

Posted on

离线分析java内存

如题,我这里简单说下我现在离线分析java内存的方式,所谓离线,就是需要dump出正在运行的java系统中的一些运行时堆栈数据,然后拿到线下来分析,分析可以包括内存,线程,GC等等,同时不会对正在运行的生产环境的机器造成很大的影响,对应着离线分析,当然是在线分析了,这个我在后面会尝试下,因为离线分析有些场景还是模拟不出来,需要借助LR来模拟压力,查看在线的java程序运行情况了。

        首先一个简单的问题,如何dump出java运行时堆栈,这个SUN就提供了很好的工具,位于JAVA_HOME/bin目录下的jmap(java memory map之意),如果需要dump出当前运行的java进程的堆栈数据,则首先需要获得该java进程的进程ID,在linux下可以使用

Java代码

  1. ps -aux
  2. ps -ef | grep java

或者使用jdk自带的一个工具jps,例如

Java代码

  1. /JAVA_HOME/bin/jps

找到了当前运行的java进程的id后,就可以对正在运行的java进程使用jmap工具进行dump了,例如使用以下命令:

Java代码

  1. JAVA_HOME/bin/jmap -dump:format=b,file=heap.bin

其中file = heap.bin的意思是dump出的文件名叫heap.bin, 当然你可以选择你喜欢的名字,我这里选择叫/*.bin是为了后面使用方便,表示你需要dump的java进程的id。

这里需要注意的是,记住dump的进程是java进程,不会是jboss的进程,weblogic的进程等。dump过程中机器load可能会升高,但是在我这里测试发现load升的不是特别快,同时dump时需要的磁盘空间也比较大,例如我这里测试的几个系统,分别是500M 800M 1500M 3000M,所以确保你运行jmap命令时所在的目录中的磁盘空间足够,当然现在的系统磁盘空间都比较大。

以上是在java进程还存活的时候进行的dump,有的时候我们的java进程crash后,会生成一个core.pid文件,这个core.pid文件还不能直接被我们的java 内存分析工具使用,需要将其转换为java 内存分析工具可以读的文件(例如使用jmap工具dump出的heap.bin文件就是很多java 内存分析工具可以读的文件格式)。将core.pid文件转换为jmap工具dump出的文件格式还可以继续使用jmap工具,这个的说明可以见我前几篇中的一个转载(Create Java heapdumps with the help of core dumps),这里我在补充点

Java代码

  1. jmap -heap:format=b [java binary] [core dump file]
  2. 64位下可以指定使用64位模式
  3. jmap -d64 -dump:format=b [java binary] [core dump file]

需要说明一下,使用jmap转换core.pid文件时,当文件格式比较大时,可能大于2G的时候就不能执行成功(我转换3G文件大小的时候没有成功)而报出

Error attaching to core file: Can't attach to the core file

查过sun的bug库中,这个bug还没有被修复,我想还是由于32位下用户进程寻址大小限制在2G的范围内引起的,在64位系统和64位jdk版本中,转换3G文件应该没有什么大的问题(有机会有环境得需要测试下)。如果有兴趣分析jmap转换不成功的同学,可以使用如下命令来分析跟踪命令的执行轨迹,例如使用

Java代码

  1. strace jmap -heap:format=b [java binary] [core dum
  2. p]

    对于strace的命令的说明,同样可以参考我前几篇文章中的一个 strace命令用法

同时对于core.pid文件的调试我也补充一下, 其中>>表示命令提示符

Java代码

  1. gdb JAVA_HOME/bin/java core.pid

  2. bt

bt后就可以看到生成core.pid文件时,系统正在执行的一个操作,例如是哪个so文件正在执行等。

好了说了这么多,上面都是怎么生成java 运行期DUMP文件的,接下来我们就进入分析阶段,为了分析这个dump出的文件,需要将这个文件弄到你的分析程序所在的机器上,例如可以是windows上,linux上,这个和你使用的分析工具以及使用的操作系统有关。不管使用什么系统,总是需要把生产环境下打出的dump文件搞到你的分析机器上,由于dump出的文件经常会比较大,例如达到2G,这么大的文件不是很好的从生产环境拉下来,因此使用FTP的方式把文件拖到分析机器上,同时由于单个文件很大,因此为了快速的将文件下载到分析机器,我们可以使用分而治之的思想,先将文件切割为小文件下载,然后在合并为一个大文件即可,还好linux提供了很方便的工具,例如使用如下命令

Java代码

  1. $ split -b 300m heap.bin
  2. $ cat x/* > heap.bin

在上面的 split 命令行中的 “300m” 表示分割后的每个文件为 300MB,“heap.bin” 为待分割的dump文件,分割后的文件自动命名为 xaa,xab,xac等 cat 命令可将这些分割后的文件合并为一个文件,例如将所有x开头的文件合并为heap.bin

如果我们是利用一个中间层的FTP服务器来保存数据的,那么我们还需要连接这个FTP服务器把合并后的文件拉下来,在windows下我推荐使用一个工具,速度很快而且简单,

winscp http://winscp.net/eng/docs/lang:chs

好了分析的文件终于经过一翻周折到了你的分析机器上,现在我们就可以使用分析工具来分析这个dump出的程序了,这里我主要是分析内存的问题,所以我说下我选择的内存分析工具,我这里使用的是开源的由SAP 和IBM 支持的一个内存分析工具

Memory Analyzer (MAT)

http://www.eclipse.org/mat/

我建议下载 Stand-alone Eclipse RCP 版本,不要装成eclipse的插件,因为这个分析起来还是很耗内存。**

下载好了,解压开来就可以直接使用了(基于eclipse的),打开以后,在菜单栏中选择打开文件,选择你刚刚的dump文件,然后一路的next就可以了,最后你会看到一个报告,这个报告里会告诉你可能的内存泄露的点,以及内存中对象的一个分布,关于mat的使用请参考官方说明,当然你也可以自己徜徉在学习的海洋中 。

对于dump文件的分析还可以使用jdk中提供的一个jhat工具来查看,不过这个很耗内存,而且默认的内存大小不够,还需要增加参数设置内存大小才能分析出,不过我看了下分析出的结果不是很满意,而且这个用起来很慢。还是推荐使用mat 。 来源: [http://dikar.iteye.com/blog/643436](http://dikar.iteye.com/blog/643436)

摘文:Create Java heapdumps with the help of core dumps

转载自:http://devops-abyss.blogspot.com/2010/03/create-java-heapdumps-with-help-of-core.html

Hi, for some time I had the problem, that taking Java heap dumps with jmap took too long. When one of my tomcats crashed by an OutOfMemoryException, I had no time to do a heap dump because it took some hours and the server had to be back online. Now I found a sollution to my problem. The initial idea came from this post. It had a solution for Solaris, but with some googling and try and error I found a solution for linux too.

  1. create a core dump of your java process with gdb gdb --pid=[java pid] gcore [file name] detach quit
  2. restart the tomcat or do whatever you like with the java process
  3. attach jmap to the core dump and create a Java heap dump jmap -heap:format=b [java binary] [core dump file]
  4. analyze your Java heap dump with your prefered tool

    When you get the following error in step three: Error attaching to core file: Can't attach to the core file

This might help: In my case the error apeared because I used the wrong java binary in the jmap call. When you are not sure about your java binary, open the core dump with gdb:

gdb --core=[core dump file]

You will get an output similar to this one:

GNU gdb 6.6 Copyright (C) 2006 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i586-suse-linux"... (no debugging symbols found) Using host libthread_db library "/lib/libthread_db.so.1". warning: core file may not match specified executable file. (no debugging symbols found) Failed to read a valid object file image from memory. Core was generated by `/opt/tomcat/bin/jsvc'. /#0 0xffffe410 in _start ()

What you are looking for is in this line:

Core was generated by `/opt/tomcat/bin/jsvc'.

Call jmap with this binary and you will get a heapdump.

来源: [http://dikar.iteye.com/blog/643196](http://dikar.iteye.com/blog/643196)

离线分析—— MAT(Memory Analyzer Tool)工具入门介绍

Posted on

离线分析—— MAT(Memory Analyzer Tool)工具入门介绍

1、MAT是什么?

MAT(Memory Analyzer Tool),一个基于Eclipse的内存分析工具,是一个快速、功能丰富的JAVA heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗。使用内存分析工具从众多的对象中进行分析,快速的计算出在内存中对象的占用大小,看看是谁阻止了垃圾收集器的回收工作,并可以通过报表直观的查看到可能造成这种结果的对象。

![](http://hi.csdn.net/attachment/201104/2/0_13017260876isZ.gif)

2.为什么使用MAT? 当服务器应用占用了过多内存的时候,会遇到OutOfMemoryError。如何快速定位问题呢?Eclipse MAT的出现使这个问题变得非常简单。它能够离线分析dump的文件数据。 Eclipse MAT是SAP公司贡献的一个工具,可以在Eclipse网站下载到它,完全免费的。它可比Sun提供的内存镜像分析工具jhat要强太多了。 3. 首页:http://www.eclipse.org/mat/![](http://blog.csdn.net/fenglibing/article/details/images/icons/linkext7.gif) 插件更新地址: http://download.eclipse.org/mat/1.0/update-site/

先调用jdk的工具得到heap使用情况

我安装的是jdk1.6

C:/>java -version

java version "1.6.0_11"

Java(TM) SE Runtime Environment (build 1.6.0_11-b03)

Java HotSpot(TM) Client VM (build 11.0-b16, mixed mode, sharing)

调用jdk工具jps查看当前的java进程

C:/>jps

3504 Jps

3676 Bootstrap

3496 org.eclipse.equinox.launcher_1.0.201.R35x_v20090715.jar

调用jmap工具得到信息

C:/>jmap -dump:format=b,file=heap.bin 3676

Dumping heap to C:/heap.bin ...

Heap dump file created

这时,我们的C盘根目录,就生成了heap.bin文件,用eclipse的file---->open打开这个文件,首先是一个启动图:

这里可以选择查看

1、内存泄露报表,自动检查可能存在内存泄露的对象,通过报表展示存活的对象以及为什么他们没有被垃圾收集;

2、对象报表,对可颖对象的分析,如字符串是否定义重了,空的collection、finalizer以及弱引用等。

我这里选择的是查看内存报表,以下是截的简略图:

通过报表展示,蛮清楚的,下面还有详细的说明,这里就没有帖图了,有兴趣的可以继续探究。 来源: [http://blog.csdn.net/fenglibing/article/details/6298326](http://blog.csdn.net/fenglibing/article/details/6298326)

内存离线分析——jhat命令(Java Heap Analyse Tool)

Posted on

内存离线分析——jhat命令(Java Heap Analyse Tool)

jhat命令 -- Java Head Analyse Tool 用途:是用来分析java堆的命令,可以将堆中的对象以html的形式显示出来,包括对象的数量,大小等等,并支持对象查询语言 第一步:导出堆 第二步:分析堆文件 第三步:查看html

有时你dump出来的堆很大,在启动时会报堆空间不足的错误,可以使用如下参数: jhat -J-Xmx512m 对于jhat启动后显示的html页面中功能: (1)显示出堆中所包含的所有的类 (2)从根集能引用到的对象 (3)显示平台包括的所有类的实例数量 (4)堆实例的分布表

(5)执行对象查询语句 更多关于对象查询语言的信息,见这篇文章: http://blog.csdn.net/gtuu0123/archive/2010/11/27/6039592.aspx done

jvm crash 的崩溃日志详细分析及注意点

Posted on

jvm crash 的崩溃日志详细分析及注意点

生成

  1. 生成error 文件的路径:你可以通过参数设置-XX:ErrorFile=/path/hs_error%p.log, 默认是在java运行的当前目录 [default: ./hs_err_pid%p.log]

  2. 参数-XX:OnError 可以在crash退出的时候执行命令,格式是-XX:OnError=“string”, 可以是命令的集合,用分号做分隔符, 可以用"%p"来取到当前进程的ID. 例如:

// -XX:OnError="pmap %p" // show memory map

// -XX:OnError="gcore %p; dbx - %p" // dump core and launch debugger

在linux中系统会fork出一个子进程去执行shell的命令,因为是用fork可能会内存不够的情况,注意修改你的 /proc/sys/vm/overcommit_memory 参数,不清楚为什么这里不使用vfork

  1. -XX:+ShowMessageBoxOnError 参数,当jvm crash的时候在linux里会启动gdb 去分析和调式,适合在测试环境中使用。

什么情况下不会生成error文件

linux 内核在发生OOM的时候会强制kill一些进程, 可以在/var/logs/messages中查找

Error crash 文件的几个重要部分

a. 错误信息概要

[plain] view plaincopy

  1. /# A fatal error has been detected by the Java Runtime Environment:
  2. /#
  3. /# SIGSEGV (0xb) at pc=0x0000000000043566, pid=32046, tid=1121192256
  4. /#
  5. /# JRE version: 6.0_17-b04
  6. /# Java VM: Java HotSpot(TM) 64-Bit Server VM (14.3-b01 mixed mode linux-amd64 )
  7. /# Problematic frame:
  8. /# C 0x0000000000043566
  9. /#
  10. /# If you would like to submit a bug report, please visit:
  11. /# http://java.sun.com/webapps/bugreport/crash.jsp
  12. /# The crash happened outside the Java Virtual Machine in native code.
  13. /# See problematic frame for where to report the bug.
    SIGSEGV 错误的信号类型

pc 就是IP/PC寄存器值也就是执行指令的代码地址

pid 就是进程id

/# Problematic frame:

/# V [libjvm.so+0x593045]

就是导致问题的动态链接库函数的地址

pc 和 +0x593045 指的是同一个地址,只是一个是动态的偏移地址,一个是运行的虚拟地址

b.信号信息

Java中在linux 中注册的信号处理函数,中间有2个参数info, ucvoid

[cpp] view plaincopy

  1. static void crash_handler(int sig, siginfo_t/ info, void/ ucVoid) {
  2. // unmask current signal
  3. sigset_t newset;
  4. sigemptyset(&newset);
  5. sigaddset(&newset, sig);
  6. sigprocmask(SIG_UNBLOCK, &newset, NULL);
  7. VMError err(NULL, sig, NULL, info, ucVoid);
  8. err.report_and_die();
  9. }
    在crash report中的信号错误提示

[plain] view plaincopy

  1. siginfo:si_signo=SIGSEGV: si_errno=0, si_code=1 (SEGV_MAPERR), si_addr=0x0000000000043566
    信号的详细信息和si_addr 出错误的内存,都保存在siginfo_t的结构体中,也就是信号注册函数crash_handler里的参数info,内核会保存导致错误的内存地址在用户空间的信号结构体中siginfo_t,这样在进程在注册的信号处理函数中可以取得导致错误的地址。

c.寄存器信息

[plain] view plaincopy

  1. Registers:
  2. RAX=0x00002aacb5ae5de2, RBX=0x00002aaaaf46aa48, RCX=0x0000000000000219, RDX=0x00002aaaaf46b920
  3. RSP=0x0000000042d3f968, RBP=0x0000000042d3f9c8, RSI=0x0000000042d3f9e8, RDI=0x0000000045aef9b8
  4. R8 =0x0000000000000f80, R9 =0x00002aaab3d30ce8, R10=0x00002aaaab138ea1, R11=0x00002b017ae65110
  5. R12=0x0000000042d3f6f0, R13=0x00002aaaaf46aa48, R14=0x0000000042d3f9e8, R15=0x0000000045aef800
  6. RIP=0x0000000000043566, EFL=0x0000000000010202, CSGSFS=0x0000000000000033, ERR=0x0000000000000014
  7. TRAPNO=0x000000000000000e
    寄存器的信息就保存在b部分的信号处理函数参数 (ucontext_t/*)usVoid中

在X86架构下:

[cpp] view plaincopy

  1. void os::print_context(outputStream /st, void /context) {
  2. if (context == NULL) return;
  3. ucontext_t /uc = (ucontext_t/)context;
  4. st->print_cr("Registers:");
  5. /#ifdef AMD64
  6. st->print( "RAX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RAX]);
  7. st->print(", RBX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RBX]);
  8. st->print(", RCX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RCX]);
  9. st->print(", RDX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RDX]);
  10. st->cr();
  11. st->print( "RSP=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RSP]);
  12. st->print(", RBP=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RBP]);
  13. st->print(", RSI=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RSI]);
  14. st->print(", RDI=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RDI]);
  15. st->cr();
  16. st->print( "R8 =" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R8]);
  17. st->print(", R9 =" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R9]);
  18. st->print(", R10=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R10]);
  19. st->print(", R11=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R11]);
  20. st->cr();
  21. st->print( "R12=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R12]);
  22. st->print(", R13=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R13]);
  23. st->print(", R14=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R14]);
  24. st->print(", R15=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R15]);
  25. st->cr();
  26. st->print( "RIP=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RIP]);
  27. st->print(", EFL=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EFL]);
  28. st->print(", CSGSFS=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_CSGSFS]);
  29. st->print(", ERR=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_ERR]);
  30. st->cr();
  31. st->print(" TRAPNO=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_TRAPNO]);
  32. /#else
  33. st->print( "EAX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EAX]);
  34. st->print(", EBX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EBX]);
  35. st->print(", ECX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_ECX]);
  36. st->print(", EDX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EDX]);
  37. st->cr();
  38. st->print( "ESP=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_UESP]);
  39. st->print(", EBP=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EBP]);
  40. st->print(", ESI=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_ESI]);
  41. st->print(", EDI=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EDI]);
  42. st->cr();
  43. st->print( "EIP=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EIP]);
  44. st->print(", CR2=" INTPTR_FORMAT, uc->uc_mcontext.cr2);
  45. st->print(", EFLAGS=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EFL]);
  46. /#endif // AMD64
  47. st->cr();
  48. st->cr();
  49. intptr_t /sp = (intptr_t /)os::Linux::ucontext_get_sp(uc);
  50. st->print_cr("Top of Stack: (sp=" PTR_FORMAT ")", sp);
  51. print_hex_dump(st, (address)sp, (address)(sp + 8/*sizeof(intptr_t)), sizeof(intptr_t));
  52. st->cr();
  53. // Note: it may be unsafe to inspect memory near pc. For example, pc may
  54. // point to garbage if entry point in an nmethod is corrupted. Leave
  55. // this at the end, and hope for the best.
  56. address pc = os::Linux::ucontext_get_pc(uc);
  57. st->print_cr("Instructions: (pc=" PTR_FORMAT ")", pc);
  58. print_hex_dump(st, pc - 16, pc + 16, sizeof(char));
  59. }
    寄存器的信息在分析出错的时候是非常重要的 打印出执行附近的部分机器码

[plain] view plaincopy

  1. Instructions: (pc=0x00007f48f14ef51a)
  2. 0x00007f48f14ef4fa: 90 90 55 48 89 e5 48 81 ec 98 9f 00 00 48 89 bd
  3. 0x00007f48f14ef50a: f8 5f ff ff 48 89 b5 f0 5f ff ff b8 00 00 00 00
  4. 0x00007f48f14ef51a: c7 00 01 00 00 00 c6 85 00 60 ff ff ff c9 c3 90
  5. 0x00007f48f14ef52a: 90 90 90 90 90 90 55 48 89 e5 53 48 8d 1d 94 00
    在instruction 部分中会打印出部分的机器码

格式是

[cpp] view plaincopy

  1. 地址:机器码
    第一种使用udis库里带的udcli工具来反汇编

命令:

[cpp] view plaincopy

  1. echo '90 90 55 48 89 e5 48 81 ec 98 9f 00 00 48 89 bd' | udcli -intel -x -64 -o 0x00007f48f14ef4fa
    显示出对应的汇编

第二种可以用

[plain] view plaincopy

  1. objectdump -d -C libjvm.so >> jvmsodisass.dump

查找偏移地址 0x593045, 就是当时的执行的汇编,然后结合上下文,源码推测出问题的语句。

d.寄存器对应的内存的值

[cpp] view plaincopy

  1. RAX=0x0000000000000000 is an unknown value
  2. RBX=0x000000041a07d1e8 is an oop
  3. {method}
    • klass: {other class}
  4. RCX=0x0000000000000000 is an unknown value
  5. RDX=0x0000000040111800 is a thread
  6. RSP=0x0000000041261b88 is pointing into the stack for thread: 0x0000000040111800
  7. RBP=0x000000004126bb20 is pointing into the stack for thread: 0x0000000040111800
  8. RSI=0x000000004126bb80 is pointing into the stack for thread: 0x0000000040111800
  9. RDI=0x00000000401119d0 is an unknown value
  10. R8 =0x0000000040111c40 is an unknown value
  11. R9 =0x00007f48fcc8b550: in /usr/java/jdk1.6.0_30/jre/lib/amd64/server/libjvm.so at 0x00007f48fc206000
  12. R10=0x00007f48f8ca7d41 is an Interpreter codelet
  13. method entry point (kind = native) [0x00007f48f8ca7ae0, 0x00007f48f8ca8320] 2112 bytes
  14. R11=0x00007f48fc98f270: in /usr/java/jdk1.6.0_30/jre/lib/amd64/server/libjvm.so at 0x00007f48fc206000
  15. R12=0x0000000000000000 is an unknown value
  16. R13=0x000000041a07d1e8 is an oop
  17. {method}
    • klass: {other class}
  18. R14=0x000000004126bb88 is pointing into the stack for thread: 0x0000000040111800
  19. R15=0x0000000040111800 is a thread
    jvm 会通过寄存器的值对找对应的对象,也是一个比较好的参考

e. 其他的信息

error 里面还有一些线程信息,还有当时内存映像信息,这些都可以作为分析的部分参考

crash 报告可以大概的反应出一个当时的情况,特别是在没有core dump的时候,是比较有助于帮助分析的,但如果有core dump的话,最终还是core dump能快速准确的发现问题原因。 来源: [http://blog.csdn.net/raintungli/article/details/7642575/#comments](http://blog.csdn.net/raintungli/article/details/7642575#comments)