天天看点

普通Java程序员学习使用的6个JDK内建工具

解决问题的最终进展来自科学、强化清晰的头脑和我们一路以来使用的工具。

普通Java程序员学习使用的6个JDK内建工具

你有没有留意过那些 jdk 安装附带的工具?既然那些大牛同意把那些工具加到 jdk 里,应该是有用的。

再次重申,下面虽然不是 jdk 工具完整列表,但是我们想给你一个精华版本。下面是你用这些命令可以完成的真正有用的事情。

0、javap

-i – 打印行数和局部变量

-p – 打印包括非public在内的所有类和成员信息,

-c – 打印方法字节码

比如在著名的“你真的懂 classloader 吗?”演讲里,当出现 nosuchmethodexception 错误时,我们可以执行以下命令来调查这个类究竟有哪些成员方法和获取这个类所有想找的信息:

javap -l -c -p util2

普通Java程序员学习使用的6个JDK内建工具

当调试类内部信息或者研究随机字节码顺序时,javap 非常有用。

1、jjs

普通Java程序员学习使用的6个JDK内建工具

jjs命令可以启动一个 javascript 命令终端,你可以把它当做计算器或者用随机的js字符串测试js的古怪用法。不要让另一个 javascript 谜题让你措手不及!

哈,看到刚刚发生了什么了么?但是 javascript 是另一个话题,只需要知道即使没有 node.js 或浏览器你也可以用jjs知道js是怎么工作的。

2、jhat

java堆分析工具(jhat)正如它名字描述的那样:分析dump堆信息。在下面的小例子里,我们构造了一个 outofmemoryerror

,然后给这个 java 进程指定 -xx:+heapdumponoutofmemoryerror ,这样运行时就会产生一个 dump

文件供我们分析。

public class ohmymemory { 

private static map map = new hashmap<>(); 

public static void main(string[] args) { 

   runtime.getruntime().addshutdownhook( 

     new thread() { 

       @override 

       public void run() { 

         system.out.println("we have accumulated " + map.size() + " entries"); 

       } 

     } 

   ); 

   for(int i = 0; ;i++) { 

     map.put(integer.tobinarystring(i), i); 

   } 

产生一个 outofmemoryerror 很简单(大部分情况下我们无意为之),我们只要不断地制造不让垃圾回收器起作用就可以了。

运行这段代码会产生如下输出:

org.shelajev.throwaway.jdktools.ohmymemory 

java.lang.outofmemoryerror: java heap space 

dumping heap to java_pid5644.hprof ... 

heap dump file created [73169721 bytes in 0.645 secs] 

exception in thread "main" java.lang.outofmemoryerror: java heap space 

at java.util.hashmap.resize(hashmap.java:703) 

at java.util.hashmap.putval(hashmap.java:662) 

at java.util.hashmap.put(hashmap.java:611) 

at org.shelajev.throwaway.jdktools.ohmymemory.main(ohmymemory.java:24) 

at sun.reflect.nativemethodaccessorimpl.invoke0(native method) 

at sun.reflect.nativemethodaccessorimpl.invoke(nativemethodaccessorimpl.java:62) 

at sun.reflect.delegatingmethodaccessorimpl.invoke(delegatingmethodaccessorimpl.java:43) 

at java.lang.reflect.method.invoke(method.java:483) 

at com.intellij.rt.execution.application.appmain.main(appmain.java:134) 

we have accumulated 393217 entries 

不错,我们现在有一个可供分析的文件了。我们对这个文件执行jhat开始进行分析,jhat 会分析这个文件开启一个 http 服务器供我们查看结果。

$ jhat java_pid5644.hprof 

reading from java_pid5644.hprof... 

dump file created thu aug 14 14:48:19 eest 2014 

snapshot read, resolving... 

resolving 1581103 objects... 

chasing references, expect 316 dots... 

eliminating duplicate references........ 

snapshot resolved. 

started http server on port 7000 

server is ready. 

可以通过访问 http://localhost:7000 来查看 dump 的数据。

普通Java程序员学习使用的6个JDK内建工具

在那个页面我们可以通过堆信息的柱状图了解究竟是什么耗尽了内存。

普通Java程序员学习使用的6个JDK内建工具

现在我们可以清晰地看到拥有 393567 结点的 hashmap 就是导致程序崩溃的元凶。虽然有更多可以检查内存分布使用情况和堆分析的工具,但是jhat是内置的,是分析的一个好的开端。

3、jmap

jmap 是一个内存映射工具,它提供了另外一种不需要引发 outofmemoryerrors 就可以获取堆 dump 文件的方法。我们稍微修改一下上面的程序看一下效果。

         try { 

           system.out.println("enter something, so i'll release the process"); 

           system.in.read(); 

           system.out.println("we have accumulated " + map.size() + " entries"); 

         } 

         catch (ioexception e) { 

           e.printstacktrace(); 

   for(int i = 0; i < 10000 ;i++) { 

注意,现在我们不要消耗大量的内存,只是比较早结束并在进程关闭钩子里等待不让 jvm 退出。这样就允许我们用 jmap 连接这个进程获取珍贵的内存 dump。

因此你可以用 jmap 的两个功能来实现,获取堆统计信息和触发一个堆 dump。因此,当执行:

jmap -heap 1354(这里 1354 是上面程序运行的进程号),就可以获取一个很好的内存使用统计信息:

$ jmap -heap 1354                                                                                                                   

attaching to process id 1354, please wait... 

debugger attached successfully. 

server compiler detected. 

jvm version is 25.0-b70 

using thread-local object allocation. 

parallel gc with 4 thread(s) 

heap configuration: 

   minheapfreeratio         = 40 

   maxheapfreeratio         = 70 

   maxheapsize              = 67108864 (64.0mb) 

   newsize                  = 1572864 (1.5mb) 

   maxnewsize               = 22020096 (21.0mb) 

   oldsize                  = 45088768 (43.0mb) 

   newratio                 = 2 

   survivorratio            = 8 

   metaspacesize            = 21807104 (20.796875mb) 

   compressedclassspacesize = 1073741824 (1024.0mb) 

   maxmetaspacesize         = 17592186044415 mb 

   g1heapregionsize         = 0 (0.0mb) 

heap usage: 

ps young generation 

eden space: 

   capacity = 1048576 (1.0mb) 

   used     = 628184 (0.5990829467773438mb) 

   free     = 420392 (0.40091705322265625mb) 

   59.908294677734375% used 

from space: 

   capacity = 524288 (0.5mb) 

   used     = 491568 (0.4687957763671875mb) 

   free     = 32720 (0.0312042236328125mb) 

   93.7591552734375% used 

to space: 

   used     = 0 (0.0mb) 

   free     = 524288 (0.5mb) 

   0.0% used 

ps old generation 

   capacity = 45088768 (43.0mb) 

   used     = 884736 (0.84375mb) 

   free     = 44204032 (42.15625mb) 

   1.9622093023255813% used 

981 interned strings occupying 64824 bytes. 

$ jmap -dump:live,format=b,file=heap.bin 1354                                                                               

dumping heap to /users/shelajev/workspace_idea/throwaway/heap.bin ... 

heap dump file created 

jmap 还可以简单地触发当前堆 dump,之后可以随意进行分析。你可以像下面例子中的那样,传一个 -dump 参数给 jmap。

现在有了 dump 得到的文件 heap.bin,就可以用你喜欢的内存分析工具来分析。

4、jps

jps 是显示 java 程序系统进程(pid)最常用的工具。它与平台无关,非常好用。想象一下我们启动了上面的程序,然后想用 jmap 连接它。这个时候我们需要程序的 pid,jps 正好的派上用场。

$ jps -mlv 

5911 com.intellij.rt.execution.application.appmain org.shelajev.throwaway.jdktools.ohmymemory -xmx64m -didea.launcher.port=7535 -didea.launcher.bin.path=/applications/intellij idea 14 eap.app/contents/bin -dfile.encoding=utf-8 

5544  -dfile.encoding=utf-8 -ea -dsun.io.usecanoncaches=false -djava.net.preferipv4stack=true -djsse.enablesniextension=false -xx:+useconcmarksweepgc -xx:softreflrupolicymspermb=50 -xx:+heapdumponoutofmemoryerror -xverify:none -xbootclasspath/a:../lib/boot.jar -xms128m -xmx750m -xx:maxpermsize=350m -xx:reservedcodecachesize=225m -xx:+usecompressedoops -agentlib:yjpagent=probe_disable=*,disablealloc,disabletracing,onlylocal,disableexceptiontelemetry,delay=10000,sessionname=intellijidea14 -didea.java.redist=nojavadistribution -didea.home.path=/applications/intellij idea 14 eap.app/contents -didea.paths.selector=intellijidea14 

5930 sun.tools.jps.jps -mlvv -dapplication.home=/library/java/javavirtualmachines/jdk1.8.0.jdk/contents/home -xms8m 

我们发现大多数情况下,“-mlv” 参数组合起来最好用。它会打印main方法的参数、完整包名、jvm 相关参数。这样你就可以在一大堆相似的进程中找到你想要的那个。

5、jstack

jstack 是一个生成指定 jvm 进程的线程堆栈工具。当你程序一直在那里转圈圈,而你想找到线程到底做了什么导致死锁,那么 jstack 最适合。

jstack 只有几个参数选项,如果你拿不准,把它们都加上。如果后面发现有些信息对你意义不大时可以调整参数限制它的输出。

-f 选项可以用来强制 dump,这在进程挂起时非常有用,-i 选项可以打印同步和锁的信息。

$ jstack -f -l 9153 

attaching to process id 9153, please wait... 

deadlock detection: 

no deadlocks found. 

…. 

上面的输出虽然看起来简单,但是它包含了每个线程的状态和它当前的堆栈的信息。

jstack 非常有用,我们在日常工作中使用非常频繁,特别是我们负责启动停止应用服务器的测试引擎。测试工作往往不顺利,jstack 可以让我们知道 jvm 内部的运行状态且没有什么负面的影响。

— neeme praks(zeroturnaround资深产品工程师)

还有其它的吗?

今天我们介绍了 jdk 发行预装的超棒工具。相信我,将来某天你肯定会用到它们中的一些。所以,如果你有时间,你可以翻一翻它们的官方文档。

试着在不同的场景使用并爱上它们。

如果你想学一些超棒的非 jdk 附带的工具,可以看看 jrebel ,它可以让你马上看到代码的改动效果,还可以看到我们新的产品 xrebel ,它可以像x光眼镜一样扫描你的 web 应用。

如果你知道开发最佳实践中至关重要的小工具,在本文末尾发表评论或者在 twitter上@shelajev 分享一下这个工具的细节。

bonus section: references

奖励环节:参考

下面是一个更加完整的 jdk 工具可用列表。虽然这不是一个完整的列表,为了节省篇幅,我们省掉了加密、web-services 相关的工具等。谢谢 manpagez.com 提供的资源。

来源:51cto