Java是一种非常强大的编程语言,自问世以来就广受欢迎。作为现今十分流行的移动平台——Android的核心语言,它大大促进了移动通信行业的发展。因此可以肯定,随着Android平台的不断扩张,Java开发人员的需求量也会不断增加。
这篇文章为大家介绍了多款有用的调试工具,以供参考。不过,在此之前,先来看看有哪些不同类型的调试器。
调试器的类型
- CLI调试器(命令行调试器):GDB、JBD,等等。
- IDE调试器(集成开发环境调试器):NetBeans、Eclipse,等等。
- 可构建的调试器:JSwat
- 堆转储:jmap、MAT
- 历史调试
- 动态跟踪
- 生产调试
好,下面就让我将这些卓越的调试工具一一道来。
1. JDB
Java Debugger(JDB)是一款高效的调试器,能够识别和修复Java程序中的bug。它也是一个用于Java类的简单的命令行调试器。此调试器包含的Java平台调试器体系结构(JPDA)可加强检查和调试本地或远程的JVM(Java虚拟机)。
JDB也是流行的Java IDE,包括NetBeans IDE和Eclipse IDE来支持Java调试,因此,非常有助于Java开发。
官方网站:http://docs.oracle.com/javase/7/docs/technotes/tools/windows/jdb.html
2. Bugloo
这款源代码级的调试器是专门设计用于支持Scheme编译器——Bigloo的。因此,使用Bigloo编译器的程序可以高效地使用Bugloo调试器进行调试。当然,不仅仅是Bigloo程序,还有Java程序或者两者的结合都可以使用此调试工具。它部署在提供了一套标准API的JVM调试架构上。此专家级软件还包含了与GDB相同的调试模型,并提供众多的功能来帮助我们应对当代编程语言中的功能问题。
官方网站:http://www-sop.inria.fr/teams/mimosa/fp/Bugloo/
3.Cricket Cage
又一个多功能的调试器,通过自动生成重复性bug和测试场景的JUnit测试用例来加强对Java程序的调试。因此,它可以通过扩展生成代码来提高我们的工作效率。这更进一步地允许安装代码生成器和执行开发相应测试用例的程序。此外,你还可以通过整合测试用例到build.xml文件中来重复每一个构建的测试。
官方网站:http://cricketcage.sourceforge.net/
4. Artima SuiteRunner
这是一款发布在开源软件许可证下的免费开源工具包,非常适合用于测试Java程序。Artima SuiteRunner可当作独立的测试工具,用来开发支持Java API的单元测试和一致性测试。并且,它还可以和JUnit一起来执行现有的JUnit测试套件。 JUnit用户还可享受到由调试器提供的帮助以确保代码万无一失。它提供报告、配置文件和runpath给用户,同时坚持实现用户开发的JUnit测试用例。
官方网站:http://www.artima.com/suiterunner/
5. FindBugs
FindBugs是一款执行静态分析Java代码来搜寻bug的软件。该软件是免费的,并且发布在GNU较宽松公共许可证及其条款下。该调试器支持JDK和JRE 1.5.0或更高的版本。此外,此工具还可用于分析编译为Java版本1.0到1.7的程序。FindBugs软件正在不断的改善中,以确保与更高版本的平台兼容以及支持最新的版本。
官方网站:http://findbugs.sourceforge.net/
NetBeans Profiler
NetBeans中可以找到NetBeans Profiler。
NetBeans分析器是NetBeans的扩展,用于为NetBeans IDE提供分析功能。NetBeans IDE是一个开源的集成开发环境。NetBeans IDE支持开发所有Java应用程序类型(Java SE(包括JavaFX),Java ME,Web,EJB和移动应用程序)。
该分析器包括CPU,内存和线程分析等功能,还提供基本JVM监控的工具和功能。对于需要解决内存或性能相关问题的开发人员来说,这是非常有用的。
JProfiler
JProfiler是一个非常好的Java分析器。将CPU,内存和线程分析结合到一个应用程序中。可用于分析性能瓶颈,内存泄漏,CPU负载和解决线程问题。支持本地分析(分析在安装了JProfiler软件的同一台机器上运行的应用程序)和远程分析(这是可以分析在未安装JProfiler远程机器上运行的Java应用程序)。
JProfiler是由ej-technologies GmbH开发的商业的Java分析工具,主要用于与Java EE和Java SE应用程序一起使用。
GC查看器
GC查看器的截图
GC Viewer可以免费下载,并且开源。可用来可视化JVM的数据(使用vmflags -verbose:gc和-Xloggc:配置)GC Viewer可用于计算与垃圾回收相关的性能指标,包括吞吐量,累积暂停,最长暂停等。当通过更改生成大小或设置初始堆大小来调整应用程序的垃圾回收时,这非常有用。
VisualVM
VisualVM是从NetBeans平台派生的工具,模块化架构,意味着它可以通过使用插件来扩展。
Visual VM允许在Java虚拟机(JVM)上运行时获取有关Java应用程序的详细信息。生成的数据可以由JDK工具生成和检索,并且可以快速查看多个Java应用程序的所有数据和信息,包括本地和远程的应用程序。也可以保存和捕获有关JVM软件的数据,并将数据保存到本地,然后再查看数据或与其他人共享数据。
Visual VM可以执行CPU分析,内存分析,运行垃圾收集,快照等。
Java性能分析工具(Patty)* Beta
Patty在行动(图片最初来自http://patty.sourceforge.net。)
“Patty”项目是可从Source Forge的开源项目,旨在为Java 1.5.0及更高版本的虚拟机提供分析工具。Patty非常重视目标分析,这是与其他分析器的区别,还允许用户在运行时切换分析功能。
目前它处于beta状态,但是关于方法执行的分析,代码覆盖,线程争用等一些强大的功能可用于分析开销(内存CPU等)。可以把信息通过TCP / IP套接字发送到其他计算机。有一个易于使用的GUI界面,可用于堆的分析。
JRockit – 任务控制
JRockit是一个专有的Java虚拟机(JVM)。最初由Appeal Virtual Machines开发,并于2002年被BEA Systems收购,后来通过Sun Microsystems成为Oracle的一部分。
Oracle JRockit是Java SE的完整解决方案,包括高性能JVM,分析,监控和诊断工具,可用于预测Java应用程序中的延迟。
JRockit的当前迭代版本与一组名为JRockit Mission Control的工具捆绑在一起。这些工具能够可视化垃圾回收和其他性能统计信息,还有用于管理的控制台,。也可以用作运行时性能分析工具,称为运行时分析器,也可以分析内存问题。
Eclipse内存分析器
内存分析器(MAT)在Eclipse IDE中找到。
Eclipse内存分析器是一个Java堆分析器,可帮助你查找内存泄漏并减少内存消耗。它更适合作为分析Java堆转储和计算其大小的通用工具包。它也可以用来报告泄漏嫌疑和内存消耗的反模式。
Java Interactive Profiler
JIP是一种高性能,低开销的分析器。它目前根据BSD许可证发行 ,可从Source Forge下载 。对于使用JIP的开发人员,可以在VM运行时打开和关闭分析器,可以过滤类和包,并控制输出。
Profiler4J
Profiler4j是专用的CPU分析器。界面友好,支持远程分析,并可以“即时”配置。值得注意的功能包括基于动态字节码的检测,它不需要本地库,也不需要可执行文件。Profilter4J可以提供带有调用图,调用树,内存监视器和类列表的图形信息,支持细粒度的配置。它目前在Apache许可证v2.0下发布, 可从Source Forge 下载 。
最后的思考
Java调试是一个很复杂的领域。市场上可选择的调试器类型也不少。希望你能够根据自己的业务目标和编程范围选择最适合的调试工具。
Jtest是Parasoft公司推出的一款针对java语言的自动化白盒测试工具,它通过自动实现java的单元测试和代码标准校验,来提高代码的可靠性。
Abbot是一款测试Java GUIs的JUnit扩展程序。
JProfiler是一个全功能的Java剖析工具(profiler),主要用于检查和跟踪系统(限于Java开发的)的性能的工具。
AppLoader是一款负载测试和功能测试软件,模拟数百万个最终用户与应用程序的交互。
Agitator帮助开发者创建和维护所有代码的单元测试。
Cactus是一个基于JUnit框架的简单测试框架,用来单元测试服务端Java代码。Cactus框架的主要目标是能够单元测试服务端的使用Servlet对象的Java方法如HttpServletRequest,HttpServletResponse,HttpSession等
测试Java程序的覆盖率工具,支持J2ME, J2SE, and J2EE。
Java代码覆盖率测试工具。
开源的Java桌面应用程序,旨在加载测试功能行为和衡量绩效。
Java源代码分析器。
HttpUnit是一个开源的测试工具,是基于JUnit的一个测试框架,主要关注于测试Web应用,解决使用JUnit框架无法对远程Web内容进行测试的弊端。HttpUnit提供的帮助类让测试者可以通过Java类和服务器进行交互,并且将服务器端的响应当作文本或者DOM对象进行处理。
是通用的测试 java 程序的测试框架JUnit可以对Java代码进行白盒测试。通过JUnitk可以用mock objects进行隔离测试;用Cactus进行容器内测试;用Ant和Maven进行自动构建;在Eclipse内进行测试;对Java应用程序、Filter、Servlet、EJB、JSP、数据库应用程序、Taglib等进行单元测试。
JDK的bin包下除了我们常用javac,java外,还提供了许多给开发人员使用的命令行工具,如图(JDK1.7):
一、javah命令(C Header and Stub File Generator)
二、jps命令(Java Virtual Machine Process Status Tool)
四、jstat命令(Java Virtual Machine Statistics Monitoring Tool)
六、jinfo命令(Java Configuration Info)
七、jconsole命令(Java Monitoring and Management Console)
八、jvisualvm命令(Java Virtual Machine Monitoring, Troubleshooting, and Profiling Tool)
九、jhat命令(Java Heap Analyse Tool)
1、介绍
jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息,如果是在64位机器上,需要指定选项”-J-d64″,Windows的jstack使用方式只支持以下的这种方式:
jstack [-l] pid
如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。
2、命令格式
jstack [ option ] pid
jstack [ option ] executable core
jstack [ option ] [server-id@]remote-hostname-or-IP
3、常用参数说明
1)、options:
executable Java executable from which the core dump was produced.
(可能是产生core dump的java可执行程序)
core 将被打印信息的core dump文件
remote-hostname-or-IP 远程debug服务的主机名或ip
server-id 唯一id,假如一台主机上多个远程debug服务
2)、基本参数:
-F当’jstack [-l] pid’没有相应的时候强制打印栈信息
-l长列表. 打印关于锁的附加信息,例如属于java.util.concurrent的ownable synchronizers列表.
-m打印java和native c/c++框架的所有栈信息.
-h | -help打印帮助信息
pid 需要被打印配置信息的java进程id,可以用jps查询.
4、使用示例
一、jstatd
启动jvm监控服务。它是一个基于rmi的应用,向远程机器提供本机jvm应用程序的信息。默认端口1099。
实例:jstatd -J-Djava.security.policy=my.policy
my.policy文件需要自己建立,内如如下:
- grant codebase “file:${java.home}/../lib/tools.jar” {
- permission java.security.AllPermission;
- };
这是安全策略文件,因为jdk对jvm做了jaas的安全检测,所以我们必须设置一些策略,使得jstatd被允许作网络操作
二、jps
列出所有的jvm实例
实例:
jps
列出本机所有的jvm实例
jps 192.168.0.77
列出远程服务器192.168.0.77机器所有的jvm实例,采用rmi协议,默认连接端口为1099
(前提是远程服务器提供jstatd服务)
输出内容如下:
jones@jones:~/data/ebook/java/j2se/jdk_gc$ jps
6286 Jps
6174 Jstat
详细请看连接:http://www.blogjava.net/aoxj/archive/2007/12/29/171447.html
三、jconsole
用法: jconsole [ -interval=n ] [ -notile ] [ -pluginpath ] [ -version ] [ connection …]
-interval 将更新间隔时间设置为 n 秒(默认值为 4 秒)
-notile 最初不平铺显示窗口(对于两个或更多连接)
-pluginpath 指定 jconsole 用于查找插件的路径
-version 输出程序版本
connection = pid || host:port || JMX URL (service:jmx:://…)
pid 目标进程的进程 ID
host 远程主机名或 IP 地址
port 用于远程连接的端口号
-J 对正在运行 jconsole 的 Java 虚拟机指定输入参数
在cmd中输入命令:jconsole 3980[java进程号] 弹出下图
四、jinfo
用法:
jinfo [ option ] pid
jinfo [ option ] executable core
jinfo [ option ] [server-id@]remote-hostname-or-IP
参数:
pid 进程号
executable 产生core dump的java executable
core core file
remote-hostname-or-IP 主机名或ip
server-id 远程主机上的debug server的唯一id
选项:
no option 打印命令行参数和系统属性
-flags 打印命令行参数
-sysprops 打印系统属性
-h 帮助
观察运行中的java程序的运行环境参数:参数包括Java System属性和JVM命令行参数
实例:
jinfo 2083
其中2083就是java进程id号,可以用jps得到这个id号。我在windows上尝试输入这个命令,但是不管用,于是我输入了下面这个命令:
jinfo -flag MaxPermSize 3980
显示如下:
-XX:MaxPermSize=67108864
五、jstack
该命令应该如何使用呢?首先需要用jstack命令产生java进程的dump文件,然后分析dump文件中的数据,下面的连接是一篇讲述如何分析jstack产生的dump文件数据的文章,写的非常不错:
http://www.blogjava.net/jzone/articles/303979.html
六、jmap(linux下特有,也是很常用的一个命令)
观察运行中的jvm物理内存的占用情况。
参数如下:
-heap:打印jvm heap的情况
-histo:打印jvm heap的直方图。其输出信息包括类名,对象数量,对象占用大小。
-histo:live :同上,但是只答应存活对象的情况
-permstat:打印permanent generation heap情况
命令使用:
jmap -heap 2083
可以观察到New Generation(Eden Space,From Space,To Space),tenured generation,Perm Generation的内存使用情况
jmap -histo 2083 | jmap -histo:live 2083
可以观察heap中所有对象的情况(heap中所有生存的对象的情况)。包括对象数量和所占空间大小。
jmap -histo java进程id
可以查看java进程中的所有实例、实例的个数,可用于查询单例对象是否真的只生成了一个实例。
在控制台,输入命令“jmap -histo 7329 > histo_dump”,得到如下结果:
- num #instances #bytes class name
- ———————————————
- 1: 605348 68849960 [C
- 2: 1609734 51511488 java.util.concurrent.ConcurrentHashMap$Segment
- 3: 1610022 38640528 java.util.concurrent.locks.ReentrantLock$NonfairSync
- 4: 70784 31478168 [I
- 5: 218224 27628072 <constMethodKlass>
- 6: 1609734 26423552 [Ljava.util.concurrent.ConcurrentHashMap$HashEntry;
- 7: 40379 24430792 [B
- 8: 218224 19211544 <methodKlass>
- 9: 602848 14468352 java.lang.String
- 10: 19374 11640184 <constantPoolKlass>
- 11: 236950 11451216 <symbolKlass>
- 12: 283746 11349840 java.util.concurrent.ConcurrentHashMap$ValueIterator
- 13: 19374 8826272 <instanceKlassKlass>
- 14: 100613 8048728 [Ljava.util.concurrent.ConcurrentHashMap$Segment;
- 15: 85036 7332664 [Ljava.lang.Object;
- 16: 15559 6614824 <constantPoolCacheKlass>
- 17: 78919 6313520 java.lang.reflect.Method
- 18: 103377 4962096 com.sun.tools.javac.zip.ZipFileIndexEntry
- 19: 51998 4324096 [Ljava.util.HashMap$Entry;
- 20: 100613 4024520 java.util.concurrent.ConcurrentHashMap
- 21: 157136 3771264 java.util.concurrent.ConcurrentHashMap$HashEntry
- 22: 35932 3736928 com.asiainfo.dbm.core.bean.monitor.FlowService
- 23: 35932 3736928 com.asiainfo.dbm.core.bean.monitor.FlowService
七、jstat
最后要重点介绍下这个命令,这是jdk命令中比较重要,也是相当实用的一个命令,可以观察到classloader,compiler,gc相关信息。
具体参数如下:
-class:统计class loader行为信息
-compile:统计编译行为信息
-gc:统计jdk gc时heap信息
-gccapacity:统计不同的generations(不知道怎么翻译好,包括新生区,老年区,permanent区)相应的heap容量情况
-gccause:统计gc的情况,(同-gcutil)和引起gc的事件
-gcnew:统计gc时,新生代的情况
-gcnewcapacity:统计gc时,新生代heap容量
-gcold:统计gc时,老年区的情况
-gcoldcapacity:统计gc时,老年区heap容量
-gcpermcapacity:统计gc时,permanent区heap容量
-gcutil:统计gc时,heap情况
-printcompilation:不知道干什么的,一直没用过。
一般比较常用的几个参数是:
- jstat -class 27629 3000 10 //每隔1秒监控一次,一共做10次
输出内容含义如下:
Loaded | Bytes | Unloaded | Bytes | Time |
49955 | 49701.5 | 35528 | 32456.1 | 34.71 |
49955 | 49701.5 | 35528 | 32456.1 | 34.71 |
49955 | 49701.5 | 35528 | 32456.1 | 34.71 |
49955 | 49701.5 | 35528 | 32456.1 | 34.71 |
49963 | 49708.8 | 35528 | 32456.1 | 34.71 |
49963 | 49708.8 | 35528 | 32456.1 | 34.71 |
49963 | 49708.8 | 35528 | 32456.1 | 34.71 |
49971 | 49716.1 | 35528 | 32456.1 | 34.71 |
49971 | 49716.1 | 35528 | 32456.1 | 34.71 |
49971 | 49716.1 | 35528 | 32456.1 | 34.71 |
jstat -gc 2083 2000 20(每隔2秒监控一次,共做10)
输出内容含义如下:
S0C | Current survivor(存活的) space 0 capacity (KB). |
EC | Current eden space capacity (KB). |
EU | Eden space utilization (KB). |
OC | Current old space capacity (KB). |
OU | Old space utilization (KB). |
PC | Current permanent space capacity (KB). |
PU | Permanent space utilization (KB). |
YGC | Number of young generation GC Events. |
YGCT | Young generation garbage collection time. |
FGC | Number of full GC events. |
FGCT | Full garbage collection time. |
GCT | Total garbage collection time. |
监控内存使用情况 参数 (查看内存溢出相对有用)
jstat -gccause 2083 5000 (每隔5秒监控一次)
输出内容含义如下:
S0 | Survivor space 0 utilization as a percentage of the space’s current capacity. |
S1 | Survivor space 1 utilization as a percentage of the space’s current capacity. |
E | Eden space utilization as a percentage of the space’s current capacity. |
O | Old space utilization as a percentage of the space’s current capacity. |
P | Permanent space utilization as a percentage of the space’s current capacity. |
YGC | Number of young generation GC events. |
YGCT | Young generation garbage collection time. |
FGC | Number of full GC events. |
FGCT | Full garbage collection time. |
GCT | Total garbage collection time. |
LGCC | Cause of last Garbage Collection. |
GCC | Cause of current Garbage Collection. |
八、jvisualvm
java visualvm 是visualvm的一个针对java vm的发布版本。 关于visualvm的更多信息可以访问 visualvm.java.net.jvisualvm 工具从jdk 6 update7 (apple的java 是从 mac os x 10.5 update 4)之后,默认就已经存在jdk工具里。
java visualvm是一个稳定的工具,用每一个jdk发布版本测试过。 最新的jdk请到oracle(sun)公司的网上进行下载。
jvisualvm 的功能及ui比jconsole还要强大。我们先来看下jvisualvm的用法。 他是一个gui(图形界面)的工具,所以上手应该会很快。
官网上关于jvisualvm的用法介绍 http://docs.oracle.com/javase/6/docs/technotes/tools/share/jvisualvm.html
简单来说,我们不需要传递任何参数就可以启动jvisualvm。
我们可以把jvisualvm也放到jdk的path里, 然后加到环境的path里。 这时候我们在windows的运行或者cmd里运行
jvisualvm就可以启动该工具了。 或者我们直接双击点击该软件
在cmd中输入命令:jvisualvm 弹出下图
具体用法可以参考下面这个链接:http://www.iteye.com/topic/516447
下面我们来讲解如何利用visualvm对远程的主机进行监控
首先,我们可以在用户目录下的任何地方(比如说:用户根目录,或者是jdk根目录)新建一个名称为jstatd.all.policy的文件,文件内容如下:
- grant codebase “file:${java.home}/../lib/tools.jar” {
- permission java.security.AllPermission;
- };
新建完文件后,我们给这个文件赋予执行权限,命令如下:
- chmod 755 jstatd.all.policy
然后,我们在我们运行如下命令,启动jstatd服务(jstatd服务的默认端口为1099):
- jstatd -J-Djava.security.policy=/sw/bes/jstatd.all.policy
记住jstatd.all.policy文件必须为绝对路径,防止出现java.security.AccessControlException: access denied (java.util.PropertyPermission java.rmi.server.ignoreSubClasses write)错误。
最后,我们可以用jps命令查看是否启动成功:
- jps -l
得到如下结果:
- 9481 com.bes.enterprise.ee.nodeagent.NodeAgentMain
- 7329 com.bes.enterprise.server.Entry
- 18968 com.bes.enterprise.server.Entry
- 15802 sun.tools.jstatd.Jstatd
- 16075 sun.tools.jps.Jps
- 9328 com.bes.enterprise.server.Entry
jps:虚拟机进程查看工具
类似于linux下的ps命令,查看进程的信息,。
-l: 输出主类的全名;
-m: 输出程序的命令行输入参数;
-v: 输出JVM参启动时显示设定的参数。
-q:只输出id
jstat:虚拟机统计信息监视工具
可以显示虚拟机加载过程当中的类加载,内存,垃圾收集,JIT编译等运行数据。
语法结构:
Usage: jstat -help|-options
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
参数解释:
Options — 选项,我们一般使用 -gcutil 查看gc情况
vmid — VM的进程号,即当前运行的java进程号
interval– 间隔时间,单位为秒或者毫秒
count — 打印次数,如果缺省则打印无数次
具体option参数如下:
-class:统计class loader行为信息(jvm参数 -XX:+TraceClassLoading和 -XX:+TraceClassUnloading 用来打印类被加载和卸载的过程信息 更详细)
-compile:统计编译行为信息
-gc:统计jdk gc时heap信息
-gccapacity:统计不同的generations(不知道怎么翻译好,包括新生区,老年区,permanent区)相应的heap容量情况
-gccause:统计gc的情况,(同-gcutil)和引起gc的事件
-gcnew:统计gc时,新生代的情况
-gcnewcapacity:统计gc时,新生代heap容量
-gcold:统计gc时,老年区的情况
-gcoldcapacity:统计gc时,老年区heap容量
-gcpermcapacity:统计gc时,permanent区heap容量
-gcutil:统计gc时,heap情况
如下:每250毫秒打印一次类加载信息,打印20次
jinfo:
实时查看和调整JVM各项参数,系统属性
用法:
jinfo [ option ] pid
jinfo [ option ] executable core
jinfo [ option ] [server-id@]remote-hostname-or-IP
参数:
pid 进程号
executable 产生core dump的java executable
core core file
remote-hostname-or-IP 主机名或ip
server-id 远程主机上的debug server的唯一id
选项:
no option 打印命令行参数和系统属性
-flags 打印命令行参数
-sysprops 打印系统属性
-h 帮助
=====================================================================
附jdb详细使用:
使用JDB调试Java程序
Java程序中有逻辑错误,就需要使用JDB来进行调试了。调试程序在IDE中很方便了,比如这篇博客介绍了在Intellj IDEA中调试Java程序的方法。
我们课程内容推荐在Linux环境下学习,有同学问如何在命令行下调试Java程序,我们就要使用JDB了。
学习建议:Linux Bash下打开三个标签页
我们提倡在Linux命令行下学习Java编程。学习时在Ubuntu Bash中通过Ctrl+Shift+T
快捷键打开三个标签(tab),:一个使用vim编辑代码;一个使用javac, java(或ant, gradle…)编译运行代码;一个使用JDB调试代码。
如下图所示,这样就不用在一个窗口中进行编辑,编译运行和调试的切换了,能提高效率。
如上图, 我们在Linux Bash中输入 vim HelloJDB.java
编辑调试示例代码:
1 public class HelloJDB {
2 public static void main(String[] args) {
3 int i = 5;
4 int j = 6;
5 int sum = add(i, j);
6 System.out.println(sum);
7
8 sum = 0;
9 for(i=0; i< 100; i++)
10 sum += i;
11
12 System.out.println(sum);
13 }
14
15 public static int add(int augend, int addend){
16 int sum = augend + addend;
17 return sum;
18 }
19}
代码编辑完,我们按“:w”进行保存而不是“:wq”进行保存退出,这样在编译或调试中遇到问题就可以按Alt+1
进入第一个标签修代码了。
我们按Alt+2
进入第二个标签,使用javac -g -d bin src/HelloJDB.java
对程序进行编译。注意javac中-g参数是为了产生各种调试信息,一定要加上,否则无法调试。
我们按Alt+3
进入第三个标签,使用jdb -classpath .:./bin HelloJDB
对程序进行调试。
调试基础
调试程序先要学会设置断点,这样才能让程序停在你感觉有问题的代码处进行排查。学习调试我们要学会设置四种断点:
- 方法断点
- 行断点
- 条件断点
- 临时断点
我们在JDB中输入help
可以查看命令列表:
上图中的stop in 用来设置方法断点,stop at 设置行断点。学习过程中要经常查看帮助文档。
上图汉化有个错误,stepi
下面的下一步
应该是next
命令,这两个都是单步执行命令,我们后面会解释step
和next
的区别。
我们通过运行stop in HelloJDB.main
命令在main方法开始处设置断点:
如上图,我们输入run
命令来运行HellJDB.class,程序会在main()的开始处停下。
此时可以用locals命令查看变量,用step命令运行下一行代码:
看两条locals
命令的结果,开始只有main方法的参数args,后面就有局部变量i,j的值了。
不使用locals
命令,我们可以使用print
或eval
命令来查看变量的值:
我们可以使用list
来查看运行到了源代码的什么位置,HelloJDB.class文件和HelloJDB.java不在同一个文件夹下,我们需要使用use
或sourcepath
指出源代码的位置,下图中的箭头指出代码运行到了哪一行:
大家注意上图是将要运行第五行,但还没有运行。还要注意,第五行是个方法调用。我们继续输入step
和list
,我们发现代码跳入16行方法体中了:
一般说来,调试时遇到方法调用,我们先看调用结果对不对,结果正确,说明方法没有问题,就不用进入方法体了; 方法调用结果不对,我们才需要进入方法体进行调试。单步跟踪命令next
和step
在执行一般语句时没有区别,在执行有方法调用的语句时,next
会把方法执行完,step
会进入方法体。所以在调试时,单步执行我们要优先使用next
,这样效率比较高。
现在已经进入方法体了,我们可以运行step up
把方法执行完,返回调用处,后面执行一般语句,你发现next
和step
没有区别。
第九行和第十行是个循环,这两条语句单步执行起来有点费劲。
我们可以通过stop at HelloJDB:12
在第12行设个断点,然后运行cont
就会一下子把循环运行完并停在第十二行。cont是continue的缩写,功能是运行到下一个断点处停止。
我们可以用stop
或clear
命令查看设置的断点的情况。
其实这里最好用个临时断点,还有,如果第9行问题出在i=80处,我们就需要条件断点,可惜JDB不支持临时断点和条件断点。
我们使用quit
或exit
可以退出JDB。
类的调试
递归的学习
递归算法是一种直接或间接地调用自身的算法。在编写程序时,递归算法对解决一大类问题是十分有效的,它往往使算法的描述简洁而且易于理解。
递归用于解决形式相同,规模不同的问题,能用递归解决的问题都可以转化为循环。递归把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。用递归思想写出的程序往往十分简洁易懂。
递归程序有两个要点:递归公式和结束条件。我们以求整数的阶乘为例:
有了公式,代码就容易写出来了:
1 public class Factorial {
2 public static void main(String [] args) {
3 System.out.println(fact(5));
4 }
5
6 public static int fact(int n) {
7 if (n == 0)
8 return 1;
9 else
10 return n * fact(n-1);
11 }
12 }
fact(5)的递推过程如下图:
我们设置好断点:
方法调用一次就会形成一个栈帧,我们在JDB中用where显示栈帧,用up,down可以在栈帧之间跳转。
大家用up,down 体会一下压栈,出栈:
多线程的调试
Java API的学习
JDB 不但是个好的调试工具,也是一个好的学习工具,可以让你了解程序的动态执行过程。
其他
JDB没有GDB那么强大,如果想使用GDB调试Java代码,以参考用GDB 调试Java程序。
=======================================================================
JDB无源代码调试调用方法
启动Xdebug调试
java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -jar ZKM.jar
JDB无源代码连接(如果是同一台电脑,需要新开一个cmd)
jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=8000
在com.zelix.lh类的a方法设置断点
stop in com.zelix.lh.a
run命令,运行到断点会停下
run
参考资料
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/14190.html