Java 程序调试时必须先要设置plsql断点调试程序吗

在线/不重启/不暂停的对Java应用进行Debug,通过程序打断点并输出断点信息
平时我们进行时经常需要通过Debug的方式查看代码运行到某一行时的上下文变量信息,以便进行代码调式分析。但是在生产环境下,正在线上运行的程序如果发现有问题需要进行调式,如果手动SocketRemote连接并设置断点并跟踪断点信息,会造成程序阻塞,影响线上正在运行的业务。本文将通过 JDI技术,介绍如何在程序正常运行的情况下,通过Java代码在不阻塞程序正常运行的情况下获取某一个代码行执行时的上下文变量信息,可以动态打断点并输出断点信息。
在介绍JDI之前,首先看一下传统的Socket Remote Debug
使用IDE进行Debug,可以使用IDE中内嵌的Tomcat进行调式,或使用Socket Remote方式调式,例如在Intellij IDEA中可以参照如下方式为程序设置远程调式端口:
启动上面的程序。之后设置进行debug的代码,如下设置要debug远程的地址和端口:
配置好后,按照如下方法启动debug即可:
JPDA(Java Platform Debugger Architecture) 是 Java 平台调试体系结构的缩写,通过 JPDA 提供的API,开发人员可以方便灵活的搭建 Java 调试应用程序。JPDA 主要由三个部分组成:Java 工具接口(JVMTI),Java调试线协议(JD),以及 Java 调试接口(JDI)。JDI(Java Debug Interface)是 JPDA三层模块中最高层的接口,定义了调试器(Debugger)所需要的一些调试接口。基于这些接口,调试器可以及时地了解目标虚拟机的状态,例如查看目标虚拟机上有哪些类和实例等。另外,调试者还可以控制目标虚拟机的执行,例如挂起和恢复目标虚拟机上的线程,设置断点等。
使用JDI编写代码进行远程调式
1. 被调式程序设置调式端口
被调式程序启动时添加允许远程调式的启动参数:
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
2. 引用类库
每个Java JDK都有自己的JDI接口实现,标准的 JDK的JDI接口放在jdk/lib/tools.jar中,因此写代码时首先需要引入这个jar。
3. 连接远程JVM
* 连接远程JVM
* @param hostname
* @param port
* @throws Exception
* @author 李文锴
private VirtualMachine connectVirtualMachine(String hostname, Integer port) throws Exception{
VirtualMachineManager vmm = Bootstrap.virtualMachineManager();
List connectors = vmm.attachingConnectors();
SocketAttachingConnector sac =
for(AttachingConnector ac : connectors){
if(ac instanceof SocketAttachingConnector){
sac = (SocketAttachingConnector)
if (sac == null) throw new Exception(&未找到可用的SocketAttachingConnector连接器&);
Map arguments = sac.defaultArguments();
arguments.get(&hostname&).setValue(hostname);
arguments.get(&port&).setValue(String.valueOf(port));
return sac.attach(arguments);
4. 标记断点
如下代码,需指定要标记断点的类和行数。注意一个类或者行可能会有多个线程来调用,但是示例代码中为了简单考虑只获取了第一个调用线程的结果,即get(0)。此外,给行打断点时,所设置的行数必须是有效代码的行。例如如果所设置的行是回车没有Java代码,则会抛出异常。
* 标记断点
* @param clazz
* @param line
* @throws Exception
* @author 李文锴
*/ public void markBreakpoint(Class clazz, Integer line) throws Exception{ EventRequestManager eventRequestManager = vm.eventRequestManager(); List rt = vm.classesByName(clazz.getName()); if(rt == null || rt.isEmpty()) throw new Exception(&无法获取有效的debug类&); ClassType classType = (ClassType) rt.get(0); List locations = classType.locationsOfLine(line); if(locations == null || locations.isEmpty()) throw new Exception(&无法获取有效的debug行&); Location location = locations.get(0); BreakpointRequest breakpointRequest = eventRequestManager.createBreakpointRequest(location); breakpointRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); breakpointRequest.enable(); }如上注册了一个Breakpoint类型的事件请求,而除了提供断点事件外还提供了很多其他的事件类型:
不同的事件需要被分类地添加到不同的事件集合(EventSet)中,事件集是事件发送的最小单位。事件集一旦创建出来,便不可再被修改。生成的事件将被依次地加入到目标虚拟机的事件队列(EventQueue)中。然后,EventQueue将这些事件集以&先进先出&策略依次地发送到调试器端。EventQueue 负责管理来自目标虚拟机的事件,一个被调试的目标虚拟机上有且仅有一个EventQueue实例。特别地,随着一次事件集的发送,目标虚拟机上可能会有一部分的线程因此而被挂起。如果一直不恢复这些线程,有可能会导致目标虚拟机挂机。因此,在处理好一个事件集中的事件后,建议调用事件集的resume() 方法,恢复所有可能被挂起的线程。
5. 跟踪断点
如下代码,获取事件时需要根据事件类型进行相应的处理:
* 跟踪断点
* @throws Exception
* @author 李文锴
*/ public void traceBreakpoint() throws Exception{ EventQueue eventQueue = vm.eventQueue(); try { boolean runable = while (runable) { EventSet eventSet = eventQueue.remove(); EventIterator eventIterator = eventSet.eventIterator(); while (eventIterator.hasNext()) { Event event = eventIterator.next(); if (event instanceof BreakpointEvent) logBreadkpoint((BreakpointEvent) event); // 断开链接肯定是最后一个事件,所以自动跳出最内层循环 if (event instanceof VMDisconnectEvent) runable = } eventSet.resume(); } } catch (Exception e) { throw new RuntimeException(e); } }
6. 获取断点信息
如下代码:
private void logBreadkpoint(BreakpointEvent event) throws Exception{
ThreadReference threadReference = event.thread();
StackFrame stackFrame = threadReference.frame(0);
logger.log(&&&================&& & + stackFrame.location()
+ & / & + stackFrame.location().method().name()
+ & &&================&&&);
logger.log(&[方法参数]&);
List argsList = stackFrame.getArgumentValues();
for (Value arg : argsList){
logger.log(arg.type().name() + & = & + parseValue(arg));
logger.log(&[变量信息]&);
List varList = stackFrame.visibleVariables();
for(LocalVariable localVariable : varList){
logLocalVariable(stackFrame, localVariable);
} catch (AbsentInformationException e) {
throw new RuntimeException(e);
private void logLocalVariable(StackFrame stackFrame, LocalVariable localVariable){
StringBuilder out = new StringBuilder(localVariable.name());
if(localVariable.isArgument()){
out.append(&[*]&);
out.append(& = &);
out.append(parseValue(stackFrame.getValue(localVariable)));
logger.log(out.toString());
调用 BreakpointEvent 的 thread() 可以获取产生事件的线程镜像(ThreadReference),调用ThreadReference 的 frame(int) 可获得当前代码行所在的堆栈(StackFrame),调用 StackFrame 的visibleVariables() 可获取当前堆栈中的所有本地变(LocaleVariable)。通过调用 BreakpointEvent 的location() 可获得断点所在的代码行号(Location),调用 Location 的 method()可获得当前代码行所归属的方法。通过以上调用,调试器便可获得了目标虚拟机上线程、对象、变量等镜像信息。
7. 解析获取的变量
private String parseValue(Value value){
StringBuilder out = new StringBuilder();
if(value instanceof StringReference || value instanceof IntegerValue || value instanceof BooleanValue
|| value instanceof ByteValue || value instanceof CharValue || value instanceof ShortValue
|| value instanceof LongValue || value instanceof FloatValue || value instanceof DoubleValue){
out.append(value);
}else if(value instanceof ObjectReference){
ObjectReference obj = (ObjectReference)
String type = obj.referenceType().name();
if(&java.lang.Integer&.equals(type) || &java.lang.Boolean&.equals(type) || &java.lang.Float&.equals(type)
|| &java.lang.Double&.equals(type) || &java.lang.Long&.equals(type) || &java.lang.Byte&.equals(type)
|| &java.lang.Character&.equals(type)){
Field f = obj.referenceType().fieldByName(&value&);
out.append(obj.getValue(f));
}else if(&java.util.Date&.equals(type)) {
Field field = obj.referenceType().fieldByName(&fastTime&);
Date date = new Date(Long.parseLong(&& + obj.getValue(field)));
out.append(new SimpleDateFormat(&yyyy-MM-dd HH:mm:ss&).format(date));
}else if(value instanceof ArrayReference){
ArrayReference ar = (ArrayReference)
List values = ar.getValues();
out.append(&[&);
for (int i = 0; i & values.size(); i++){
if( i != 0 ) out.append(& ,&);
out.append(parseValue(values.get(i)));
out.append(&]&);
out.append(type);
return out.toString();
Value 和 Type 接口分别代表着目标虚拟机中对象、实例变量和方法变量的值和类型。通过 Value 接口的 type(),可以获取该值对应的类型。JDI中定义了两种基本的数据类型:原始类型(PrimitiveType)和引用类(ReferenceType)。与其对应的数值类型分别是原始值(PrimtiveValue)和对象引用(ObjectReference),Value和 Type 的具体对应关系如下:
8. 测试:启动目标程序
测试目标类如下,计划在System.out.println行打断点,为了演示方便通过Thread.sleep添加暂停事件,以便启动调式程序:
public class Test {
public static void main(String[] args) throws Exception {
test(&hello&, 100);
private static void test(String a, Integer b) throws Exception{
Thread.sleep(10*1000);
Boolean isOk =
Date date = new Date();
System.out.println(&over&);
启动时添加远程debug参数并运行。
9. 测试:执行debug程序
代码如下:
public class Debug {
public static void main(String[] args) throws Exception{
DebugRemoteJava drj = new DebugRemoteJava(&127.0.0.1&, 5005);
drj.markBreakpoint(Test.class, 18);
drj.traceBreakpoint();
输出结果如下:JAVA中如何添加断点调试_百度知道
该问题可能描述不清,建议你
JAVA中如何添加断点调试
添加断点测试可以直接在要测试的代码处,双击鼠标左键,出现一个蓝色小点,说明添加断点成功;接下来是测试,在代码页面点击鼠标右键,选择debug -&debug as myeclipse/eclipse application。以debug方式运行java程序后,可以执行以下操作:(F5)单步执行程序,遇到方法时进入;(F6)单步执行程序,遇到方法时跳过;(F7)单步执行程序,从当前方法跳出;(F8)直接执行程序。遇到断点时暂停。另外,在debug时,会有很多有用信息显示在debug框里,如堆栈信息,需要自己实践;在程序界面里,鼠标移到变量上时会有当前变量的属性值。可看下参考资料(有图说明):。
采纳率:30%
不知道你是用的软件来编程的,一般来说,在程序需要添加断点的地方,在左侧显示行数的地方点一下,会出现一个红点,这就是断点,鼠标放上去显示为breakpoint
本回答被提问者和网友采纳
在你需要断点的地方的行数处双击出现一个蓝色的小圆点,即断点设置成功,然后右键 Debug As 选择运行的方式 运行
会出现Debug调试的试图,然后根据试图左上方的标识,根据自己的实际情况,进行下一步 或是跳过调试的话主要是提示你代码出错的地方,在排查问题这方面挺方便的
eclipse 中debug功能。
其他1条回答
为您推荐:
其他类似问题
您可能关注的内容
断点的相关知识
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。java单元测试与断点测试
1 Junit与单元测试
JUnit 单元测试必须针对某一个单元
Assert Assert.assertEquals(&&,&&);相同则JUnit通过,否则不通过
断点调试(debug)+watch变量适时值
测试的类中不可以有有参构造方法
程序示例:
import org.junit.A
import org.junit.T
public class JUnit {
public static void main(String[] args){
String b=&hiperfect&;
System.out.println(b);
public void a(){&
String aa=
System.out.println(aa);
String expecteds=&1&;
String actuals=&1&;
Assert.assertEquals(expecteds, actuals);
//测试中还常常用到断点调试(特别是参数的watch)
2 断点测试
断点大家都比较熟悉,在Eclipse
编辑区的行头双击就会得到一个断点,代码会运行到此处时停止。
1.条件断点:
只有满足了用户设置的条件,代码才会在运行到断点处时停止。&
在断点处点击鼠标右键,选择最后一个&Breakpoint Properties&&
2.变量断点:
断点不仅能打在语句上,变量也可以接受断点,
在变量的值初始化,或是变量值改变时可以停止,当然变量断点上也是可以加条件的,和上面的介绍的条件断点的设置是一样的。&
3.方法断点:
方法断点就是将断点打在方法的入口处,&
方法断点的特别之处在于它可以打在 JDK的里,由于 JDK 在编译时去掉了调试信息,所以普通断点是不能打到里面的,但是方法断点却可以,可以通过这种方法查看方法的调用栈。&
4.改变变量值:
代码停在了断点处,但是传过来的值不正确,如何修改一下变量值保证代码继续走正确的流程,或是说有一个异常分支老是进不去,能不能调试时改一下条件,看一下异常分支代码是否正确?&
在Debug 视图的 Variables 小窗口中,我们可以看到 mDestJarName 变量的值为 & F:\Study\eclipsepro\JarDir\jarHelp.jar & &
我们可以在变量上右键,选择&Change Value...& 在弹出的对话框中修改变量的值,&
5.重新调试:
这种调试的回退不是万能的,只能在当前线程的栈帧中回退,也就说最多只能退回到当前线程的调用的开始处。&
回退时,请在需要回退的线程方法上点右键,选择 &Drop to Frame&&
6.异常断点
经常遇见一些异常,然后程序就退出来了,要找到异常发生的地方就比较难了,还好可以打一个异常断点,如果增加了一个如NullPointException等异常断点,当异常发生时,代码会停在异常发生处,定位问题时应该比较有帮助。

我要回帖

更多关于 程序断点调试 的文章

 

随机推荐