UsefulScripts-Java脚本
本文于
1331
天之前发表,文中内容可能已经过时。
用于快速排查Java
的CPU
性能问题(top us
值过高),自动查出运行的Java
进程中消耗CPU
多的线程,并打印出其线程栈,从而确定导致性能问题的方法调用。
PS,如何操作可以参见@bluedavy 的《分布式Java应用》的【5.1.1 cpu消耗分析】一节,说得很详细:
top
命令找出有问题Java
进程及线程id
:
开启线程显示模式
按CPU
使用率排序
记下Java
进程id
及其CPU
高的线程id
用进程id
作为参数,jstack
有问题的Java
进程
手动转换线程id
成十六进制(可以用printf %x 1234
)
查找十六进制的线程id
(可以用grep
)
查看对应的线程栈
查问题时,会要多次这样操作以确定问题,上面过程太繁琐太慢了 。
用法 1 2 3 4 5 6 7 8 9 10 11 12 13 show-busy-java-threads.sh show-busy-java-threads.sh -c <要显示的线程栈数> show-busy-java-threads.sh -c <要显示的线程栈数> -p <指定的Java Process> sudo show-busy-java-threads.sh
示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 $ show-busy-java-threads.sh [1] Busy(57.0%) thread(23355/0x5b3b) stack of java process(23269) under user(admin): "pool-1-thread-1" prio=10 tid=0x000000005b5c5000 nid=0x5b3b runnable [0x000000004062c000] java.lang.Thread.State: RUNNABLE at java.text.DateFormat.format(DateFormat.java:316) at com.xxx.foo.services.common.DateFormatUtil.format(DateFormatUtil.java:41) at com.xxx.foo.shared.monitor.schedule.AppMonitorDataAvgScheduler.run(AppMonitorDataAvgScheduler.java:127) at com.xxx.foo.services.common.utils.AliTimer$2 .run(AliTimer.java:128) 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) [2] Busy(26.1%) thread(24018/0x5dd2) stack of java process(23269) under user(admin): "pool-1-thread-2" prio=10 tid=0x000000005a968800 nid=0x5dd2 runnable [0x00000000420e9000] java.lang.Thread.State: RUNNABLE at java.util.Arrays.copyOf(Arrays.java:2882) at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:100) at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:572) at java.lang.StringBuffer.append(StringBuffer.java:320) - locked <0x00000007908d0030> (a java.lang.StringBuffer) at java.text.SimpleDateFormat.format(SimpleDateFormat.java:890) at java.text.SimpleDateFormat.format(SimpleDateFormat.java:869) at java.text.DateFormat.format(DateFormat.java:316) at com.xxx.foo.services.common.DateFormatUtil.format(DateFormatUtil.java:41) at com.xxx.foo.shared.monitor.schedule.AppMonitorDataAvgScheduler.run(AppMonitorDataAvgScheduler.java:126) at com.xxx.foo.services.common.utils.AliTimer$2 .run(AliTimer.java:128) at java.util.concurrent.ThreadPoolExecutor$Worker .runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker .run(ThreadPoolExecutor.java:908) ...
上面的线程栈可以看出,CPU
消耗最高的2个线程都在执行java.text.DateFormat.format
,业务代码对应的方法是shared.monitor.schedule.AppMonitorDataAvgScheduler.run
。可以基本确定:
AppMonitorDataAvgScheduler.run
调用DateFormat.format
次数比较频繁。
DateFormat.format
比较慢。(这个可以由DateFormat.format
的实现确定。)
多个执行几次show-busy-java-threads.sh
,如果上面情况高概率出现,则可以确定上面的判定。 # 因为调用越少代码执行越快,则出现在线程栈的概率就越低。
分析shared.monitor.schedule.AppMonitorDataAvgScheduler.run
实现逻辑和调用方式,以优化实现解决问题。
贡献者
silentforce 改进此脚本,增加对环境变量JAVA_HOME
的判断。 #15
liuyangc3
优化性能,通过read -a
简化反复的awk
操作 #51
发现并解决jstack
非当前用户Java
进程的问题 #50
找出Java Lib
(Java
库,即Jar
文件)或Class
目录(类目录)中的重复类。
Java
开发的一个麻烦的问题是Jar
冲突(即多个版本的Jar
),或者说重复类。会出NoSuchMethod
等的问题,还不见得当时出问题。找出有重复类的Jar
,可以防患未然。
用法
通过脚本参数指定Libs
目录,查找目录下Jar
文件,收集Jar
文件中Class
文件以分析重复类。可以指定多个Libs
目录。 注意,只会查找这个目录下Jar
文件,不会查找子目录下Jar
文件。因为Libs
目录一般不会用子目录再放Jar
,这样也避免把去查找不期望Jar
。
通过-c
选项指定Class
目录,直接收集这个目录下的Class
文件以分析重复类。可以指定多个Class
目录。
1 2 3 4 5 6 7 8 9 10 11 show-duplicate-java-classes show-duplicate-java-classes path/to/lib_dir1 /path/to/lib_dir2 show-duplicate-java-classes -c path/to/class_dir1 -c /path/to/class_dir2 show-duplicate-java-classes path/to/lib_dir1 /path/to/lib_dir2 -c path/to/class_dir1 -c path/to/class_dir2
JDK
开发场景使用说明以Maven
作为构建工程示意过程。
对于一般的工程 1 2 3 4 5 6 $ mvn dependency:copy-dependencies -DincludeScope=runtime ... $ show-duplicate-java-classes target/dependency ...
对于Web
工程 对于Web
工程,即war
maven
模块,会打包生成war
文件。
1 2 3 4 5 6 7 8 9 $ mvn install ... $ unzip target/*.war -d target/war ... $ show-duplicate-java-classes -c target/war/WEB-INF/classes target/war/WEB-INF/lib ...
Android
开发场景使用说明Android
开发,有重复类在编译打包时会报[Dex Loader] Unable to execute dex: Multiple dex files define Lorg/foo/xxx/Yyy
。
但只会给出一个重复类名,如果重复类比较多时,上面打包/报错/排查会要进行多次,而Android
的打包比较费时,这个过程比较麻烦,希望可以一次把所有重复类都列出来,一起排查掉。
以Gradle
作为构建工程示意过程。
在App
的build.gradle
中添加拷贝库到目录build/dependencies
下。
1 2 3 4 5 6 7 8 9 10 task copyDependencies (type: Copy) { def dest = new File(buildDir, "dependencies" ) dest.deleteDir() dest.mkdirs() from configurations.compile into dest }
1 2 3 4 5 6 $ ./gradlew app:copyDependencies ... $ show-duplicate-java-classes app/build/dependencies ...
示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 $ show-duplicate-java-classes WEB-INF/lib COOL! No duplicate classes found! ================================================================================ class paths to find: ================================================================================ 1 : WEB-INF/lib/sourceforge.spring.modules.context-2.5.6.SEC02.jar 2 : WEB-INF/lib/misc.htmlparser-0.0.0.jar 3 : WEB-INF/lib/normandy.client-1.0.2.jar ... $ show-duplicate-java-classes -c WEB-INF/classes WEB-INF/lib Found duplicate classes in below class path: 1 (293@2): WEB-INF/lib/sourceforge.spring-2.5.6.SEC02.jar WEB-INF/lib/sourceforge.spring.modules.orm-2.5.6.SEC02.jar 2 (2@3): WEB-INF/lib/servlet-api-3.0-alpha-1.jar WEB-INF/lib/jsp-api-2.1-rev-1.jar WEB-INF/lib/jstl-api-1.2-rev-1.jar 3 (104@2): WEB-INF/lib/commons-io-2.2.jar WEB-INF/lib/jakarta.commons.io-2.0.jar 4 (6@3): WEB-INF/lib/jakarta.commons.logging-1.1.jar WEB-INF/lib/commons-logging-1.1.1.jar WEB-INF/lib/org.slf4j.jcl104-over-slf4j-1.5.6.jar 5 (344@2): WEB-INF/lib/sourceforge.spring-2.5.6.SEC02.jar WEB-INF/lib/sourceforge.spring.modules.context-2.5.6.SEC02.jar ... ================================================================================ Duplicate classes detail info: ================================================================================ 1 (293@2): WEB-INF/lib/sourceforge.spring-2.5.6.SEC02.jar WEB-INF/lib/sourceforge.spring.modules.orm-2.5.6.SEC02.jar 1 org/springframework/orm/toplink/TopLinkTemplate$13 .class 2 org/springframework/orm/hibernate3/HibernateTemplate$24 .class 3 org/springframework/orm/jpa/vendor/HibernateJpaDialect.class 4 org/springframework/orm/hibernate3/TypeDefinitionBean.class 5 org/springframework/orm/hibernate3/SessionHolder.class ... 2 (2@3): WEB-INF/lib/servlet-api-3.0-alpha-1.jar WEB-INF/lib/jsp-api-2.1-rev-1.jar WEB-INF/lib/jstl-api-1.2-rev-1.jar 1 javax/servlet/ServletException.class 2 javax/servlet/ServletContext.class 3 (104@2): WEB-INF/lib/commons-io-2.2.jar WEB-INF/lib/jakarta.commons.io-2.0.jar 1 org/apache/commons/io/input/ProxyReader.class 2 org/apache/commons/io/output/FileWriterWithEncoding.class 3 org/apache/commons/io/output/TaggedOutputStream.class 4 org/apache/commons/io/filefilter/NotFileFilter.class 5 org/apache/commons/io/filefilter/TrueFileFilter.class ... ... ================================================================================ class paths to find: ================================================================================ 1 : WEB-INF/lib/sourceforge.spring.modules.context-2.5.6.SEC02.jar 2 : WEB-INF/lib/misc.htmlparser-0.0.0.jar 3 : WEB-INF/lib/normandy.client-1.0.2.jar 4 : WEB-INF/lib/xml.xmlgraphics__batik-css-1.7.jar-1.7.jar 5 : WEB-INF/lib/jakarta.ecs-1.4.2.jar ...
贡献者 tgic 提供此脚本。友情贡献者的链接commandlinefu.cn |微博linux命令行精选
在当前目录下所有jar
文件里,查找类或资源文件。
用法 1 2 3 4 find-in-jars.sh 'log4j\.properties' find-in-jars.sh 'log4j\.xml$' -d /path/to/find/directory find-in-jars.sh log4j\\.xml find-in-jars.sh 'log4j\.properties|log4j\.xml'
注意,后面Pattern是grep
的 扩展 正则表达式。
示例 1 2 3 $ ./find-in-jars 'Service.class$' ./WEB-INF/libs/spring-2.5.6.SEC03.jar!org/springframework/stereotype/Service.class ./rpc-benchmark-0.0.1-SNAPSHOT.jar!com/taobao/rpc/benchmark/service/HelloService.class
参考资料 在多个Jar(Zip)文件查找Log4J配置文件的Shell命令行