与你的问题不同,我认为软件工程主要是用来解决问题的。有些博客认为“每个小孩都应该学习编程”,“你认为学数学只是玩玩而已?如果你有看过我的HTML5调试器的话,你会发现我是一个程序员,但我做的工作远不止数学这些”。 上面两者都同意一个观点,软件工程不只是用计算机语言写的一些只言片语。软件解决的问题诠释了程序员的价值。

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

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

因此,在这篇文章里,我挑了几个 Hotspot 标准安装后可用的小工具来介绍。我们决定忽略那些安全相关的和各种远程方法调用(RMI)、applets、web-start、web-services 工具。让我们把焦点放在那些普通开发者开发一般应用过程中可能有用的工具。注意,如果你只是对命令行工具感兴趣,而不仅仅是Java相关的工具,这里介绍了 5 个非常有用的命令行工具。

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

0、javap

你可以给 javap(Java Class文件反编译器)传递这些有用的参数:

  • -I – 打印行数和局部变量
  • -p – 打印包括非public在内的所有类和成员信息,
  • -c – 打印方法字节码

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

javap -l -c -p Util2

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

1、jjs

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 的数据。

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

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

3、jmap

jmap 是一个内存映射工具,它提供了另外一种不需要引发 OutOfMemoryErrors 就可以获取堆 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() {
         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++) {
     map.put(Integer.toBinaryString(i), 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:
   capacity = 524288 (0.5MB)
   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 相关参数。这样你就可以在一大堆相似的进程中找到你想要的那个。

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

5、jstack

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

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

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

$ jstack -F -l 9153
Attaching to process ID 9153, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.0-b70
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 提供的资源。

  • jar — 一个创建和管理 jar 文件的工具。
  • java — Java 应用启动器。在这篇文章里,开发和部署都是用的这个启动器。
  • javac — Java 编译器。
  • javadoc — API 文档生成器。
  • javah — native 本地方法中用于生成 C 语言头文件和源文件。
  • javap — class 文件反编译器。
  • jcmd — JVM 命令行诊断工具,可发送诊断命令请求到 JVM 中。
  • jconsole — 一个兼容 JMX 的监控 JVM 的图形化工具。可以监控本地和远程 JVM,也可以监控和管理单独的一个应用。
  • jdb — Java 调试器。
  • jps — JVM 进程查看工具,列出了系统运行的所有 hotspot JVM 进程。
  • jstat — JVM 状态监控工具。它可以收集和打印指定的 JVM 进程性能状态。
  • jhat — 堆 dump 信息的浏览器,启动一个 web 服务器来显示你用诸如 jmap -dump 得到的堆 dump 信息。
  • jmap — Java 内存映射工具,打印指定进程、核心文件、远程调试服务器共享内存映射或者堆内存详细信息。
  • jsadebugd — Java 服务调试守护进程—依附到一个 Java 进程或核心文件并且担当一个调试服务器的作用。
  • jstack —Java 堆栈信息工具——打印指定进程或核心文件或者远程调试服务器的线程堆栈。
  • jjs — 运行 Nashorn 命令行脚本 shell。
  • jrunscript — Java 脚本运行工具。不过你要心里有数,这实际上是一个还没支持的测试功能。未来的 JDK 版本里面可能会移除它。

希望上面的内容对你们有帮助,你可以在 twitter 上 @ shelajev留下你宝贵的评论。如果你知道一些我没有提及的重要工具,请让我知道。

普通Java程序员学习使用的6个JDK内建工具的更多相关文章

  1. Java程序员学习之路

    1. Java语言基础 谈到Java语 言基础学习的书籍,大家肯定会推荐Bruce Eckel的<Thinking in Java>.它是一本写的相当深刻的技术书籍,Java语言基础部分基 ...

  2. 聊聊阿里社招面试,谈谈“野生”Java程序员学习的道路

    引言 很尴尬的是,这个类型的文章其实之前笔者就写过,原文章里,笔者自称LZ(也就是楼主,有人说是老子的简写,笔者只想说,这位同学你站出来,保证不打死你,-_-),原文章名称叫做<回答阿里社招面试 ...

  3. 【Python】Java程序员学习Python(五)— 函数的定义和使用

    不想做一个待宰的羔羊!!!!要自己变得强大.... 函数的定义和使用放在最前边还是有原因的,现在语言趋于通用,基本类型基本都是那些,重点还是学习对象的使用方法,而最根本的还是方法的使用,因此优先介绍, ...

  4. 如何准备阿里社招面试,顺谈 Java 程序员学习中各阶段的建议

    引言 其实本来真的没打算写这篇文章,主要是LZ得记忆力不是很好,不像一些记忆力强的人,面试完以后,几乎能把自己和面试官的对话都给记下来.LZ自己当初面试完以后,除了记住一些聊过的知识点以外,具体的内容 ...

  5. 写给自己的Java程序员学习路线图

    恩,做开发的工作已经三年多了,说起来实在是惭愧,自己的知识树还像一棵小草一样,工作中使用到了许多的知识和技术,不过系统性不够.根基不牢.并且不够深入!当然,慢慢的我也更加的清楚,我需要学习一些什么样的 ...

  6. 【Python】Java程序员学习Python(二)— 开发环境搭建

    巧妇难为无米之炊,我最爱的还是鸡蛋羹,因为我和鸡蛋羹有段不能说的秘密. 不管学啥,都要有环境,对于程序员来说搭建个开发环境应该不是什么难题.按顺序一步步来就可以,我也只是记录我的安装过程,你也可以滴. ...

  7. 写给自己的Java程序员学习路线图_转载

    如下是我做开发这三年经常使用一些技术和工具,当然这些技术也都是需要加强的(有些是我一直使用的,不过不深入,有些内部的原理等等不是很清楚) 前端部分: 1)HTML:网页的核心语言,构成网页的基础 2) ...

  8. 【Python】Java程序员学习Python(六)— 流程控制、异常处理

    和Java语言一样,Python也有基本的流程控制,简单了解下即可. 一.流程控制的元素 条件 条件就是布尔值或者布尔值的表达式,要么是True要么是False. 代码块 在Python中,代码块不是 ...

  9. 【Python】Java程序员学习Python(三)— 基础入门

    一闪一闪亮晶晶,满天都是小星星,挂在天上放光明,好像许多小眼睛.不要问我为什么喜欢这首歌,我不会告诉你是因为有人用口琴吹给我听. 一.Python学习文档与资料 一般来说文档的资料总是最权威,最全面的 ...

随机推荐

  1. Java 中文件下载的几种应用

    public HttpServletResponse download(String path, HttpServletResponse response) { try { // path是指欲下载的 ...

  2. 打印出不同顺序的字符串&单引号和双引号的差异

    发现一个很好玩的打印顺序 package com.liaojianya.chapter1; /** * This program demonstrates the string. * @author ...

  3. 生产者与消费者(三)---BlockingQueue

    前面阐述了实现生产者与消费者问题的两种方式:wait() / notify()方法 和 await() / signal()方法,本文继续阐述多线程的经典问题---生产者与消费者的第三种方式:Bloc ...

  4. 24种设计模式--建造者模式【Builder Pattern】

    在一个周三,快要下班了,老大突然又拉住我,喜滋滋的告诉我“牛叉公司很满意我们做的模型,又签订了一个合同,把奔驰.宝马的车辆模型都交给我们公司制作了,不过这次又额外增加了一个新需求:汽车的启动.停止.喇 ...

  5. 让USB键盘的LED灯听你的!(不干扰使用)

    最近在研究一个课题,如何能利用键盘的led灯通道进行有效通信,那么首先要做的就是尝试能否在不影响键盘的情况下控制LED灯(num lock ,caps lock ,scroll lock)的使用. 首 ...

  6. PDF在xp或2003下正常在win7下乱码的问题

    1.先确定当前PDF文件需要字体(在PDF工具打开找到Font字体可以查看具体需要哪些字体). 2.网上下载或者在生成PDF的电脑上把老版本字体拷贝出来然后在win7下安装,当提示已经存在该字体时,直 ...

  7. 【算法】快速排序-Java版

    说在前面的话 平常码砖的时候,对于一个数组进行排序更多的是起泡排序,起泡排序对于一般不是很长的数组进行操作没什么问题,一旦数组过大,很明显效率低. 而快排是对起泡排序的一种改进,效率明显优高. 快排思 ...

  8. dotnet core多平台开发体验

    前言 随着net core rc2的发布,园子里面关于net core的入门文章也也多了起来,但是大多数都是在一个平台上面来写几个简单的例子,或者是在解释代码本身,并没有体现说在一个平台上面创建一个项 ...

  9. C语言存储类型及各存储类型作用域和生存域比较

    c语言中的存储类型有`auto`, `extern`, `register`,`static` 这四种,存储类型说明了该变量要在进程的哪一个段中分配内存空间,可以为变量分配内存存储空间的有数据区.BB ...

  10. Android Studio与Genymontion的安装

    需要安装的软件: VirtualBox 5.1.4 Genymotion 2.7.2 Android Studio 2.1.2 安装步骤: 使用安装版安装Android Studio 1.     执 ...