java 问题定位

下面是我们遇到过的情况,如有错误或需要补充的内容,请直接修改。 
内存异常:
? java.lang.OutOfMemoryError: PermGen space
    》resin热部署,重新加载jar包,容易产生这种问题
    》持久代设置得太小也会导致这种问题,这时候需要修改jvm参数:-XX:PermSize=32m -XX:MaxPermSize=64m
    》这种情况测试环境常见,线上基本不会出现这种问题,我们的perm内存够用,而且会开启perm GC。
? java.lang.OutOfMemoryError: Java heap space
    》一般情况是从数据库或缓存加载了大量数据或者用户上传了大量文件,还有就是我们的代码问题,集合类使用不当,作用域的问题。
    》一般来说,由于会触发GC,只要代码不存在内存泄漏问题,线上很难出现这个异常。
    》解决办法:重启、修复代码隐患
? java.lang.OutOfMemoryError: GC overhead limit exceeded
    》这是因为使用并发收集算法进行GC,并且jvm启动参数中加了-XX:-UseGCOverheadLimit选项。
    》目前只在hive的应用中遇到此情况,线上应用一般是CMS算法,不会出现这种情况。
    》解决办法:增加heap size或者禁用上面那个选项。
? 很多时候内存异常并不会表现为异常,还没有达到这个临界点,你的系统就已经不可用了,这个时候需要主动去检查内存使用情况:
    》查询GC状态,看一下jvm是否在进行GC操作:
jstat -gcutil <pid> <time_span>[@tc-152-92 ~]$ jstat -gcutil 16590 3000  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT     0.00   0.00  85.96  90.60  54.20   3336    0.781 38188 13952.038 13952.819  0.00   0.00  91.42  90.60  54.20   3336    0.781 38189 13952.565 13953.346  0.00   0.00  97.43  90.60  54.20   3336    0.781 38190 13952.960 13953.741像上面这种情况就是有问题的:Full GC次数远大于Young GC次数,很不合理。Young GC一般都很快,而Full GC至少是100ms+(线上),所以Full GC多的话我们就需要增大jvm的heap参数。    
    》查询java对象内存占用情况,看一下内存里的java对象是否合理。
[@zjm-110-88 ~]$ jmap -histo 2234 | head -10 num     #instances         #bytes  class name----------------------------------------------   1:       3373503     2209452824  [C   2:       3334031      133361240  java.lang.String   3:           260      101301344  [Lcom.caucho.util.LruCache$CacheItem;   4:        326846       63127704  [Ljava.lang.Object;   5:        151274       50828064  com.wap.sohu.mobilepaper.model.NewsContent   6:         19812       45474976  [I   7:        110209       40197776  [B   8:        145988       30902344  [Ljava.util.HashMap$Entry;   9:       1846859       29549744  java.lang.Object  10:        270121       19448712  com.wap.sohu.mobilepaper.model.xml.Image    

 ? 常用jvm参数:
        -XX:MaxPermSize=512m -XX:PermSize=512m -Xss128k
        -Xmx4096m -Xms4096m -Xmn1024m 
        -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=85 -XX:+PrintGCDetails 
        -XX:MaxTenuringThreshold=30
       
CPU异常:
? 运行的线程多了,我们的应用里有不少线程去异步地执行任务,可能某个时间点或事件触发了大量线程同时去执行操作,导致cpu资源紧张。
? 程序运行的慢了,比如大量的计算操作,频繁地进行循环遍历。
? io操作多,比如频繁地打印日志,频繁地进行网络访问(mysql,memcache)。
? 过多的同步操作。比如synchronize
? 一般情况下,我们都是通过观察jvm的栈信息来识别程序的异常,主要看java.lang.Thread.State这个值,一般BLOCKED和RUNNABLE都需要重点关注。BLOCKED状态肯定是有锁,比如频繁的IO操作会导致资源BLOCK或者我们代码里显式的加锁。RUNNABLE状态理论上是正常的,但是很有可能是逻辑处理太慢(比如网络io或计算)或调用频繁导致一段代码执行时间较长,这个也需要优化。
[@yd-80-133 ~]$ jstack 1344 2013-06-08 16:15:42Full thread dump Java HotSpot(TM) 64-Bit Server VM (20.8-b03 mixed mode): "pool-40-thread-5" prio=10 tid=0x000000005cea6800 nid=0x639c runnable [0x00000000493c5000]   java.lang.Thread.State: RUNNABLE        at java.net.SocketInputStream.socketRead0(Native Method)        at java.net.SocketInputStream.read(SocketInputStream.java:129)        at com.mysql.jdbc.util.ReadAheadInputStream.fill(ReadAheadInputStream.java:114)        at com.mysql.jdbc.util.ReadAheadInputStream.readFromUnderlyingStreamIfNecessary(ReadAheadInputStream.java:161)        at com.mysql.jdbc.util.ReadAheadInputStream.read(ReadAheadInputStream.java:189)        - locked <0x000000074c115a08> (a com.mysql.jdbc.util.ReadAheadInputStream)        at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:3014)        at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3467)        at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3456)        at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3997)        at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2468)        at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2629)        at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2719)        - locked <0x0000000770fb3380> (a com.mysql.jdbc.JDBC4Connection)        at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2155)        - locked <0x0000000770fb3380> (a com.mysql.jdbc.JDBC4Connection)        at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2450)        - locked <0x0000000770fb3380> (a com.mysql.jdbc.JDBC4Connection)        at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:2006)        - locked <0x0000000770fb3380> (a com.mysql.jdbc.JDBC4Connection)        at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:1467)        - locked <0x0000000770fb3380> (a com.mysql.jdbc.JDBC4Connection)        at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeBatch(NewProxyPreparedStatement.java:1723)        at org.springframework.jdbc.core.JdbcTemplate$4.doInPreparedStatement(JdbcTemplate.java:873)        at org.springframework.jdbc.core.JdbcTemplate$4.doInPreparedStatement(JdbcTemplate.java:1)        at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:586)        at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:614)        at org.springframework.jdbc.core.JdbcTemplate.batchUpdate(JdbcTemplate.java:858)        at com.wap.sohu.mobilepaper.dao.statistic.StatisticDao$BatchUpdateTask2.run(StatisticDao.java:285)        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)        at java.lang.Thread.run(Thread.java:662)    
? 提供一种查看消耗cpu的线程的方式:
1. 找到java进程的id号,从top命令里可以看到消耗cpu最多的那个进程。下面是进程信息:[@tc-152-92 ~]$ ps -ef | grep 18950smc      18950     1 90 Dec19 ?        21:22:46 java -server -Xmx768m -Xms768m -Xss128k -Xmn300m -XX:MaxPermSize=128m -XX:PermSize=128m -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=85 -XX:+PrintGCDetails -XX:+UseMembar -Dhost_home=/opt/smc -Dserver_log_home=/opt/smc/log/server -Xloggc:/opt/smc/log/server/check_instance_gc.log -Dserver_ip=10.11.152.92 -Dserver_resources=/opt/smc/apps/server/server_apps/check_instance/resources/ -Dserver_name=check_instance com.wap.sohu.server.SmcApiServer 8010 2. 查看当时消耗cpu最多的线程:(-p 指定java进程号, -H 显示所有线程)[@tc-152-92 ~]$ top -p 18950 -H PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 18999 smc       16   0 1676m 1.0g  14m S 32.3  6.5 268:50.16 java                                                                                                           18997 smc       16   0 1676m 1.0g  14m S 30.4  6.5 267:47.97 java                                                                                                           18998 smc       17   0 1676m 1.0g  14m S 30.4  6.5 268:15.28 java                                                                                                           19000 smc       16   0 1676m 1.0g  14m S 30.4  6.5 268:06.23 java                                                                                                           19001 smc       15   0 1676m 1.0g  14m S  5.7  6.5  81:34.02 java  3. 保留当时的堆栈信息:[@tc-152-92 ~]$ jstack 18950 > 18950.txt 4. 把上面最消耗cpu的线程ID转成十六进制:[@tc-152-92 ~]$ pythonPython 2.4.3 (#1, Jun 11 2009, 14:09:37) [GCC 4.1.2 20080704 (Red Hat 4.1.2-44)] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> hex(18999)‘0x4a37‘>>> 5. 在堆栈文件里查找这个十六进制的线程号:[@tc-152-92 ~]$ vim 18950.txt"Concurrent Mark-Sweep GC Thread" prio=10 tid=0x000000004183b800 nid=0x4a39 runnable"Gang worker#0 (Parallel CMS Threads)" prio=10 tid=0x0000000041834000 nid=0x4a35 runnable"Gang worker#1 (Parallel CMS Threads)" prio=10 tid=0x0000000041836000 nid=0x4a36 runnable"Gang worker#2 (Parallel CMS Threads)" prio=10 tid=0x0000000041837800 nid=0x4a37 runnable"Gang worker#3 (Parallel CMS Threads)" prio=10 tid=0x0000000041839800 nid=0x4a38 runnable 这里发现都是gc线程在消耗cpu,从最上面的进程信息可以看到这个java进程只开了1G的堆,top信息里显示的内存占用已经到1G了,正好能对上。    

代码里的常见异常:
? java.lang.OutOfMemoryError: unable to create new native thread
程序里起的线程太多了。一种情况是启动程序的用户(smc)最大可运行线程有限制(ulimit -a查看),另一种情况就是代码里起了很多线程。
当这种异常出现时,我们登录到服务器上当前用户下会出现Resource temporarily unavailable的错误。(如果是root用户启动的进程,就只能重启机器了)
? Broken Pipe  // TODO...
? Too many open files   // TODO...
? c3p0连接池异常:Attempted to use a closed or broken resource pool
<property name="breakAfterAcquireFailure" value="true"></property>
改成:
<property name="breakAfterAcquireFailure" value="false"></property>
如果参数为true,只要有一次获取数据库连接失败后,整个数据源就会声明为断开并永久关闭,服务就不可用了.
如果参数为false,获取数据库连接失败后程序会抛出异常,但是数据源仍然有效,等下次尝试获取连接成功后就可以正常使用了. 
? 类初始化异常
java.lang.NoClassDefFoundError: Could not initialize class com.wap.sohu.mobilepaper.util.ClientUserCenterHttpUtils
    > 可能是你的classpath路径不对,或类加载顺序的问题(jar冲突),jinfo <pid>  查看java.class.path属性
    > 类文件不存在
    > 类初始化时抛出未捕获的异常,比如static块或static变量初始化有问题。
   
在线Debug方式
安装:10.10.76.79:/root/btrace-bin.tar.gz 把这个文件拷到目标机器上,新建一个btrace的目录,解压即可用:tar -zxvf btrace-bin.tar.gz -C btrace/
修改权限:cd bin/;    chmod 744 *
编写Btrace脚本,比如我们要监控一个方法的输入参数和输出内容:
import com.sun.btrace.annotations.*;import static com.sun.btrace.BTraceUtils.*;import java.util.*;@BTracepublic class Response {  @OnMethod(clazz="com.sohu.smc.reply.core.LocalCache", method="getBulk", [email protected](Kind.RETURN))  public static void onGetBulk(String[] keys, @Return Map<String, Object> result) {    println("=======================================");    printArray(keys);    println(strcat("Params keys length:", str(keys.length)));    println(strcat("Result length:", str(size(result))));  }//    @OnMethod(clazz="com.sohu.smc.reply.core.CommentCursorList", method="getIdList", [email protected](Kind.RETURN))//    public static void onGetIdList(@Return List<Long> result) {//      println(strcat("ID LIST:", str(size(result))));//    }}    
运行这个脚本(samples目录下提供了很多例子,基本都可以直接拿来用):
[[email protected] bin]$ ./bin/btrace -cp build/ 15631 Response.java     =======================================[]Params keys length:0Result length:0=======================================[rp_482712136, rp_482665158, rp_482662804, ]Params keys length:3Result length:3    
btrace运行后会对线上jvm造成一定的影响,我们最好是重启一下jvm实例,恢复正常状态。
          btrace常用的场景有:监控方法响应时间,监控线程启动,类加载,异常信息(有时候我们的代码会吃掉异常,看不到具体哪行代码产生的)。
时间: 2024-10-30 10:52:00

java 问题定位的相关文章

Java问题定位方法总结

背景   "线下没问题的". "代码不可能有问题 是系统原因"."能在线上远程debug么"    线上问题不同于开发期间的bug,与运行时环境.压力.并发情况.具体的业务相关.对于线上的问题利用线上环境可用的工具,收集必要信息 对定位问题十分重要.    对于导致问题的bug.资源瓶颈很难直观取得数据,需要根据资源使用数据.日志等信息推测问题根源.并且疑难问题的定位通常需要使用不同的方法追根溯源.    这篇wiki我对自己使用过的工具做了整理

[selenium webdriver Java]元素定位——findElement/findElements

策略 语法 语法 描述 By id driver.findElement(By.id()) driver.findElements(By.id()) 通过id属性定位元素 By name driver.findElement(By.name()) driver.findElements(By.name()) 通过name属性定位元素 By class name driver.findElement(By.className()) driver.findElements(By.className(

Java问题定位工具

JDK本身提供了很多方便的JVM性能调优监控工具,除了集成式的VisualVM和jConsole外,还有jps.jstack.jmap.jhat.jstat.hprof等小巧的工具 现实企业级Java开发中,有时候我们会碰到下面这些问题: OutOfMemoryError,内存不足 内存泄露 线程死锁 锁争用(Lock Contention) Java进程消耗CPU过高 ...... 这些问题在日常开发中可能被很多人忽视(比如有的人遇到上面的问题只是重启服务器或者调大内存,而不会深究问题根源),

java 文件定位

java中,定位class的方式,总共有以下几种 XXX.class.getResource(String resourceName) XXX.class.getClassLoader().getResource(String resourceName) Thread.currentThread().getContextClassLoader().getResource() XXX.class.getProtectionDomain().getCodeSource().getLocation()

JAVA问题定位跟踪技术

常用的JAVA调试技巧: 线程堆栈解读 性能瓶颈分析 远程调试 内存泄露检测 常用工具集: proc工具集 系统跟踪命令truss/strace Core文件管理coreadm 进程状态监控prstat 网络状态监控netstat 磁盘监控iostat CPU和内存监控vmstat抓包工具-- 输出线程堆栈: Windows:在运行java的控制台上按ctrl+break组合键 Unix:保留启动java的控制台,使用kill -3 <pid> *:启动时进行重定向是一个不错的习惯:run.s

Java 问题定位工具 ——jstack

简介 jstack 主要用于生成虚拟机当前时刻的「线程快照」.线程快照是当前 Java 虚拟机每一条线程正在执行的方法堆栈的集合. 生成线程快照的主要目的是用于定位线程出现长时间停顿的原因,如线程间死锁.死循环.请求外部资源导致长时间等待. 基础 线程的几种状态 NEW,未启动的.不会出现在Dump中. RUNNABLE,在虚拟机内执行的. BLOCKED,受阻塞并等待监视器锁. WATING,无限期等待另一个线程执行特定操作. TIMED_WATING,有时限的等待另一个线程的特定操作. TE

java 堆 栈 方法区的简单分析

Java里的堆(heap)栈(stack)和方法区(method) 基础数据类型直接在栈空间分配, 方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收.   引用数据类型,需要用new来创建,既在栈空间分配一个地址空间,又在堆空间分配对象的类变量 . 方法的引用参数,在栈空间分配一个地址空间,并指向堆空间的对象区,当方法调用完成后从栈空间回收.局部变量 new 出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间立刻被回收,堆空间区域等待GC回收. 方法调用时传入的

Java里的堆(heap)栈(stack)和方法区(method)

基础数据类型直接在栈空间分配, 方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收.   引用数据类型,需要用new来创建,既在栈空间分配一个地址空间,又在堆空间分配对象的类变量 . 方法的引用参数,在栈空间分配一个地址空间,并指向堆空间的对象区,当方法调用完成后从栈空间回收.局部变量 new 出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间立刻被回收,堆空间区域等待GC回收. 方法调用时传入的 literal 参数,先在栈空间分配,在方法调用完成后从栈空间分配.

MacOS10.9平台配置Appium+Java环境

1) 安装JDK 下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html   安装:JDK安装很简单,按默认安装即可. 配置环境变量: 打开终端新建.bash_profile文件:touch .bash_profile 打开bash_profile文件:vi .bash_profile 配置JAVA_HOME export JAVA_HOME=$(/usr/libexec/j