侵权投诉

一文详解Java动态调试技术

2020-10-18 11:33 次阅读

调试是发现和减少计算机程序或电子仪器设备中程序错误的一个过程。最常用的断点调试技术会在断点位置停顿,导致应用停止响应。本文将介绍一种Java动态调试技术,希望能对大家有帮助。

1. 动态调试要解决的问题

断点调试是我们最常使用的调试手段,它可以获取到方法执行过程中的变量信息,并可以观察到方法的执行路径。但断点调试会在断点位置停顿,使得整个应用停止响应。在线上停顿应用是致命的,动态调试技术给了我们创造新的调试模式的想象空间。本文将研究Java语言中的动态调试技术,首先概括Java动态调试所涉及的技术基础,接着介绍我们在Java动态调试领域的思考及实践,通过结合实际业务场景,设计并实现了一种具备动态性的断点调试工具Java-debug-tool,显著提高了故障排查效率。

2. Java Agent技术

JVMTI (JVM Tool Interface)是Java虚拟机对外提供的Native编程接口,通过JVMTI,外部进程可以获取到运行时JVM的诸多信息,比如线程、GC等。Agent是一个运行在目标JVM的特定程序,它的职责是负责从目标JVM中获取数据,然后将数据传递给外部进程。加载Agent的时机可以是目标JVM启动之时,也可以是在目标JVM运行时进行加载,而在目标JVM运行时进行Agent加载具备动态性,对于时机未知的Debug场景来说非常实用。下面将详细分析Java Agent技术的实现细节。

2.1 Agent的实现模式

JVMTI是一套Native接口,在Java SE 5之前,要实现一个Agent只能通过编写Native代码来实现。从Java SE 5开始,可以使用Java的Instrumentation接口(java.lang.instrument)来编写Agent。无论是通过Native的方式还是通过Java Instrumentation接口的方式来编写Agent,它们的工作都是借助JVMTI来进行完成,下面介绍通过Java Instrumentation接口编写Agent的方法。

2.1.1 通过Java Instrumentation API

实现Agent启动方法

Java Agent支持目标JVM启动时加载,也支持在目标JVM运行时加载,这两种不同的加载模式会使用不同的入口函数,如果需要在目标JVM启动的同时加载Agent,那么可以选择实现下面的方法:

JVM将首先寻找[1],如果没有发现[1],再寻找[2]。如果希望在目标JVM运行时加载Agent,则需要实现下面的方法:

这两组方法的第一个参数AgentArgs是随同 “– javaagent”一起传入的程序参数,如果这个字符串代表了多个参数,就需要自己解析这些参数。inst是Instrumentation类型的对象,是JVM自动传入的,我们可以拿这个参数进行类增强等操作。

指定Main-Class

Agent需要打包成一个jar包,在ManiFest属性中指定“Premain-Class”或者“Agent-Class”:

挂载到目标JVM

将编写的Agent打成jar包后,就可以挂载到目标JVM上去了。如果选择在目标JVM启动时加载Agent,则可以使用 “-javaagent:《jarpath》[=《option》]”,具体的使用方法可以使用“Java -Help”来查看。如果想要在运行时挂载Agent到目标JVM,就需要做一些额外的开发了。

com.sun.tools.attach.VirtualMachine 这个类代表一个JVM抽象,可以通过这个类找到目标JVM,并且将Agent挂载到目标JVM上。下面是使用com.sun.tools.attach.VirtualMachine进行动态挂载Agent的一般实现:

首先通过指定的进程ID找到目标JVM,然后通过Attach挂载到目标JVM上,执行加载Agent操作。VirtualMachine的Attach方法就是用来将Agent挂载到目标JVM上去的,而Detach则是将Agent从目标JVM卸载。关于Agent是如何挂载到目标JVM上的具体技术细节,将在下文中进行分析。

2.2 启动时加载Agent

2.2.1 参数解析

创建JVM时,JVM会进行参数解析,即解析那些用来配置JVM启动的参数,比如堆大小、GC等;本文主要关注解析的参数为-agentlib、 -agentpath、 -javaagent,这几个参数用来指定Agent,JVM会根据这几个参数加载Agent。下面来分析一下JVM是如何解析这几个参数的。

上面的代码片段截取自hotspot/src/share/vm/runtime/arguments.cpp中的Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_mod_javabase, Flag::Flags origin) 函数,该函数用来解析一个具体的JVM参数。这段代码的主要功能是解析出需要加载的Agent路径,然后调用add_init_agent函数进行解析结果的存储。下面先看一下add_init_agent函数的具体实现:

AgentLibraryList是一个简单的链表结构,add_init_agent函数将解析好的、需要加载的Agent添加到这个链表中,等待后续的处理。

这里需要注意,解析-javaagent参数有一些特别之处,这个参数用来指定一个我们通过Java Instrumentation API来编写的Agent,Java Instrumentation API底层依赖的是JVMTI,对-JavaAgent的处理也说明了这一点,在调用add_init_agent函数时第一个参数是“instrument”,关于加载Agent这个问题在下一小节进行展开。到此,我们知道在启动JVM时指定的Agent已经被JVM解析完存放在了一个链表结构中。下面来分析一下JVM是如何加载这些Agent的。

2.2.2 执行加载操作

在创建JVM进程的函数中,解析完JVM参数之后,下面的这段代码和加载Agent相关:

当JVM判断出上一小节中解析出来的Agent不为空的时候,就要去调用函数create_vm_init_agents来加载Agent,下面来分析一下create_vm_init_agents函数是如何加载Agent的。

create_vm_init_agents这个函数通过遍历Agent链表来逐个加载Agent。通过这段代码可以看出,首先通过lookup_agent_on_load来加载Agent并且找到Agent_OnLoad函数,这个函数是Agent的入口函数。如果没找到这个函数,则认为是加载了一个不合法的Agent,则什么也不做,否则调用这个函数,这样Agent的代码就开始执行起来了。对于使用Java Instrumentation API来编写Agent的方式来说,在解析阶段观察到在add_init_agent函数里面传递进去的是一个叫做“instrument”的字符串,其实这是一个动态链接库。在Linux里面,这个库叫做libinstrument.so,在BSD系统中叫做libinstrument.dylib,该动态链接库在{JAVA_HOME}/jre/lib/目录下。

2.2.3 Instrument动态链接库

libinstrument用来支持使用Java Instrumentation API来编写Agent,在libinstrument中有一个非常重要的类称为:JPLISAgent(Java Programming Language Instrumentation Services Agent),它的作用是初始化所有通过Java Instrumentation API编写的Agent,并且也承担着通过JVMTI实现Java Instrumentation中暴露API的责任。

我们已经知道,在JVM启动的时候,JVM会通过-javaagent参数加载Agent。最开始加载的是libinstrument动态链接库,然后在动态链接库里面找到JVMTI的入口方法:Agent_OnLoad。下面就来分析一下在libinstrument动态链接库中,Agent_OnLoad函数是怎么实现的。

上述代码片段是经过精简的libinstrument中Agent_OnLoad实现的,大概的流程就是:先创建一个JPLISAgent,然后将ManiFest中设定的一些参数解析出来, 比如(Premain-Class)等。创建了JPLISAgent之后,调用initializeJPLISAgent对这个Agent进行初始化操作。跟进initializeJPLISAgent看一下是如何初始化的:

这里,我们关注callbacks.VMInit = &eventHandlerVMInit;这行代码,这里设置了一个VMInit事件的回调函数,表示在JVM初始化的时候会回调eventHandlerVMInit函数。下面来看一下这个函数的实现细节,猜测就是在这里调用了Premain方法:

看到这里,Instrument已经实例化,invokeJavaAgentMainMethod这个方法将我们的Premain方法执行起来了。接着,我们就可以根据Instrument实例来做我们想要做的事情了。

2.3 运行时加载Agent

比起JVM启动时加载Agent,运行时加载Agent就比较有诱惑力了,因为运行时加载Agent的能力给我们提供了很强的动态性,我们可以在需要的时候加载Agent来进行一些工作。因为是动态的,我们可以按照需求来加载所需要的Agent,下面来分析一下动态加载Agent的相关技术细节。

2.3.1 AttachListener

Attach机制通过Attach Listener线程来进行相关事务的处理,下面来看一下Attach Listener线程是如何初始化的。

我们知道,一个线程启动之后都需要指定一个入口来执行代码,Attach Listener线程的入口是attach_listener_thread_entry,下面看一下这个函数的具体实现:

整个函数执行逻辑,大概是这样的:

拉取一个需要执行的任务:AttachListener::dequeue。

查询匹配的命令处理函数。

执行匹配到的命令执行函数。

其中第二步里面存在一个命令函数表,整个表如下:

对于加载Agent来说,命令就是“load”。现在,我们知道了Attach Listener大概的工作模式,但是还是不太清楚任务从哪来,这个秘密就藏在AttachListener::dequeue这行代码里面,接下来我们来分析一下dequeue这个函数:

这是Linux上的实现,不同的操作系统实现方式不太一样。上面的代码表面,Attach Listener在某个端口监听着,通过accept来接收一个连接,然后从这个连接里面将请求读取出来,然后将请求包装成一个AttachOperation类型的对象,之后就会从表里查询对应的处理函数,然后进行处理。

Attach Listener使用一种被称为“懒加载”的策略进行初始化,也就是说,JVM启动的时候Attach Listener并不一定会启动起来。下面我们来分析一下这种“懒加载”策略的具体实现方案。

上面的代码截取自create_vm函数,DisableAttachMechanism、StartAttachListener和ReduceSignalUsage这三个变量默认都是false,所以AttachListener::init();这行代码不会在create_vm的时候执行,而vm_start会执行。下面来看一下这个函数的实现细节:

这是在Linux上的实现,是将/tmp/目录下的.java_pid{pid}文件删除,后面在创建Attach Listener线程的时候会创建出来这个文件。上面说到,AttachListener::init()这行代码不会在create_vm的时候执行,这行代码的实现已经在上文中分析了,就是创建Attach Listener线程,并监听其他JVM的命令请求。现在来分析一下这行代码是什么时候被调用的,也就是“懒加载”到底是怎么加载起来的。

这是create_vm中的一段代码,看起来跟信号相关,其实Attach机制就是使用信号来实现“懒加载“的。下面我们来仔细地分析一下这个过程。

JVM创建了一个新的进程来实现信号处理,这个线程叫“Signal Dispatcher”,一个线程创建之后需要有一个入口,“Signal Dispatcher”的入口是signal_thread_entry:

这段代码截取自signal_thread_entry函数,截取中的内容是和Attach机制信号处理相关的代码。这段代码的意思是,当接收到“SIGBREAK”信号,就执行接下来的代码,这个信号是需要Attach到JVM上的信号发出来,这个后面会再分析。我们先来看一句关键的代码:AttachListener::is_init_trigger():

首先检查了一下是否在JVM启动时启动了Attach Listener,或者是否已经启动过。如果没有,才继续执行,在/tmp目录下创建一个叫做.attach_pid%d的文件,然后执行AttachListener的init函数,这个函数就是用来创建Attach Listener线程的函数,上面已经提到多次并进行了分析。到此,我们知道Attach机制的奥秘所在,也就是Attach Listener线程的创建依靠Signal Dispatcher线程,Signal Dispatcher是用来处理信号的线程,当Signal Dispatcher线程接收到“SIGBREAK”信号之后,就会执行初始化Attach Listener的工作。

2.3.2 运行时加载Agent的实现

我们继续分析,到底是如何将一个Agent挂载到运行着的目标JVM上,在上文中提到了一段代码,用来进行运行时挂载Agent,可以参考上文中展示的关于“attachAgentToTargetJvm”方法的代码。这个方法里面的关键是调用VirtualMachine的attach方法进行Agent挂载的功能。下面我们就来分析一下VirtualMachine的attach方法具体是怎么实现的。

这个方法通过attachVirtualMachine方法进行attach操作,在MacOS系统中,AttachProvider的实现类是BsdAttachProvider。我们来看一下BsdAttachProvider的attachVirtualMachine方法是如何实现的:

findSocketFile方法用来查询目标JVM上是否已经启动了Attach Listener,它通过检查“tmp/”目录下是否存在java_pid{pid}来进行实现。如果已经存在了,则说明Attach机制已经准备就绪,可以接受客户端的命令了,这个时候客户端就可以通过connect连接到目标JVM进行命令的发送,比如可以发送“load”命令来加载Agent。如果java_pid{pid}文件还不存在,则需要通过sendQuitTo方法向目标JVM发送一个“SIGBREAK”信号,让它初始化Attach Listener线程并准备接受客户端连接。可以看到,发送了信号之后客户端会循环等待java_pid{pid}这个文件,之后再通过connect连接到目标JVM上。

2.3.3 load命令的实现

下面来分析一下,“load”命令在JVM层面的实现:

这个函数先确保加载了java.instrument模块,之后真正执行Agent加载的函数是 load_agent_library ,这个函数的套路就是加载Agent动态链接库,如果是通过Java instrument API实现的Agent,则加载的是libinstrument动态链接库,然后通过libinstrument里面的代码实现运行agentmain方法的逻辑,这一部分内容和libinstrument实现premain方法运行的逻辑其实差不多,这里不再做分析。至此,我们对Java Agent技术已经有了一个全面而细致的了解。

3. 动态替换类字节码技术

3.1 动态字节码修改的限制

上文中已经详细分析了Agent技术的实现,我们使用Java Instrumentation API来完成动态类修改的功能,在Instrumentation接口中,通过addTransformer方法来增加一个类转换器,类转换器由类ClassFileTransformer接口实现。ClassFileTransformer接口中唯一的方法transform用于实现类转换,当类被加载的时候,就会调用transform方法,进行类转换。在运行时,我们可以通过Instrumentation的redefineClasses方法进行类重定义,在方法上有一段注释需要特别注意:

这里面提到,我们不可以增加、删除或者重命名字段和方法,改变方法的签名或者类的继承关系。认识到这一点很重要,当我们通过ASM获取到增强的字节码之后,如果增强后的字节码没有遵守这些规则,那么调用redefineClasses方法来进行类的重定义就会失败。那redefineClasses方法具体是怎么实现类的重定义的呢?它对运行时的JVM会造成什么样的影响呢?下面来分析redefineClasses的实现细节。

3.2 重定义类字节码的实现细节

上文中我们提到,libinstrument动态链接库中,JPLISAgent不仅实现了Agent入口代码执行的路由,而且还是Java代码与JVMTI之间的一道桥梁。我们在Java代码中调用Java Instrumentation API的redefineClasses,其实会调用libinstrument中的相关代码,我们来分析一下这条路径。

这是InstrumentationImpl中的redefineClasses实现,该方法的具体实现依赖一个Native方法redefineClasses(),我们可以在libinstrument中找到这个Native方法的实现:

redefineClasses这个函数的实现比较复杂,代码很长。下面是一段关键的代码片段:

可以看到,其实是调用了JVMTI的RetransformClasses函数来完成类的重定义细节。

重定义类的请求会被JVM包装成一个VM_RedefineClasses类型的VM_Operation,VM_Operation是JVM内部的一些操作的基类,包括GC操作等。VM_Operation由VMThread来执行,新的VM_Operation操作会被添加到VMThread的运行队列中去,VMThread会不断从队列里面拉取VM_Operation并调用其doit等函数执行具体的操作。VM_RedefineClasses函数的流程较为复杂,下面是VM_RedefineClasses的大致流程:

加载新的字节码,合并常量池,并且对新的字节码进行校验工作

清除方法上的断点

JIT逆优化

进行字节码替换工作,需要进行更新类itable/vtable等操作

进行类重定义通知

VM_RedefineClasses实现比较复杂的,详细实现可以参考 RedefineClasses的实现。

4. Java-debug-tool设计与实现

Java-debug-tool是一个使用Java Instrument API来实现的动态调试工具,它通过在目标JVM上启动一个TcpServer来和调试客户端通信。调试客户端通过命令行来发送调试命令给TcpServer,TcpServer中有专门用来处理命令的handler,handler处理完命令之后会将结果发送回客户端,客户端通过处理将调试结果展示出来。下面将详细介绍Java-debug-tool的整体设计和实现。

4.1 Java-debug-tool整体架构

Java-debug-tool包括一个Java Agent和一个用于处理调试命令的核心API,核心API通过一个自定义的类加载器加载进来,以保证目标JVM的类不会被污染。整体上Java-debug-tool的设计是一个Client-Server的架构,命令客户端需要完整的完成一个命令之后才能继续执行下一个调试命令。Java-debug-tool支持多人同时进行调试,下面是整体架构图:

图4-1-1

下面对每一层做简单介绍:

交互层:负责将程序员的输入转换成调试交互协议,并且将调试信息呈现出来。

连接管理层:负责管理客户端连接,从连接中读调试协议数据并解码,对调试结果编码并将其写到连接中去;同时将那些超时未活动的连接关闭。

业务逻辑层:实现调试命令处理,包括命令分发、数据收集、数据处理等过程。

基础实现层:Java-debug-tool实现的底层依赖,通过Java Instrumentation提供的API进行类查找、类重定义等能力,Java Instrumentation底层依赖JVMTI来完成具体的功能。

在Agent被挂载到目标JVM上之后,Java-debug-tool会安排一个Spy在目标JVM内活动,这个Spy负责将目标JVM内部的相关调试数据转移到命令处理模块,命令处理模块会处理这些数据,然后给客户端返回调试结果。命令处理模块会增强目标类的字节码来达到数据获取的目的,多个客户端可以共享一份增强过的字节码,无需重复增强。下面从Java-debug-tool的字节码增强方案、命令设计与实现等角度详细说明。

4.2 Java-debug-tool的字节码增强方案

Java-debug-tool使用字节码增强来获取到方法运行时的信息,比如方法入参、出参等,可以在不同的字节码位置进行增强,这种行为可以称为“插桩”,每个“桩”用于获取数据并将他转储出去。Java-debug-tool具备强大的插桩能力,不同的桩负责获取不同类别的数据,下面是Java-debug-tool目前所支持的“桩”:

方法进入点:用于获取方法入参信息。

Fields获取点1:在方法执行前获取到对象的字段信息。

变量存储点:获取局部变量信息。

Fields获取点2:在方法退出前获取到对象的字段信息。

方法退出点:用于获取方法返回值。

抛出异常点:用于获取方法抛出的异常信息。

通过上面这些代码桩,Java-debug-tool可以收集到丰富的方法执行信息,经过处理可以返回更加可视化的调试结果。

4.2.1 字节码增强

Java-debug-tool在实现上使用了ASM工具来进行字节码增强,并且每个插桩点都可以进行配置,如果不想要什么信息,则没必要进行对应的插桩操作。这种可配置的设计是非常有必要的,因为有时候我们仅仅是想要知道方法的入参和出参,但Java-debug-tool却给我们返回了所有的调试信息,这样我们就得在众多的输出中找到我们所关注的内容。如果可以进行配置,则除了入参点和出参点外其他的桩都不插,那么就可以快速看到我们想要的调试数据,这种设计的本质是为了让调试者更加专注。下面是Java-debug-tool的字节码增强工作方式:

图4-2-1

如图4-2-1所示,当调试者发出调试命令之后,Java-debug-tool会识别命令并判断是否需要进行字节码增强,如果命令需要增强字节码,则判断当前类+当前方法是否已经被增强过。上文已经提到,字节码替换是有一定损耗的,这种具有损耗的操作发生的次数越少越好,所以字节码替换操作会被记录起来,后续命令直接使用即可,不需要重复进行字节码增强,字节码增强还涉及多个调试客户端的协同工作问题,当一个客户端增强了一个类的字节码之后,这个客户端就锁定了该字节码,其他客户端变成只读,无法对该类进行字节码增强,只有当持有锁的客户端主动释放锁或者断开连接之后,其他客户端才能继续增强该类的字节码。

字节码增强模块收到字节码增强请求之后,会判断每个增强点是否需要插桩,这个判断的根据就是上文提到的插桩配置,之后字节码增强模块会生成新的字节码,Java-debug-tool将执行字节码替换操作,之后就可以进行调试数据收集了。

经过字节码增强之后,原来的方法中会插入收集运行时数据的代码,这些代码在方法被调用的时候执行,获取到诸如方法入参、局部变量等信息,这些信息将传递给数据收集装置进行处理。数据收集的工作通过Advice完成,每个客户端同一时间只能注册一个Advice到Java-debug-tool调试模块上,多个客户端可以同时注册自己的Advice到调试模块上。Advice负责收集数据并进行判断,如果当前数据符合调试命令的要求,Java-debug-tool就会卸载这个Advice,Advice的数据就会被转移到Java-debug-tool的命令结果处理模块进行处理,并将结果发送到客户端。

4.2.2 Advice的工作方式

Advice是调试数据收集器,不同的调试策略会对应不同的Advice。Advice是工作在目标JVM的线程内部的,它需要轻量级和高效,意味着Advice不能做太过于复杂的事情,它的核心接口“match”用来判断本次收集到的调试数据是否满足调试需求。如果满足,那么Java-debug-tool就会将其卸载,否则会继续让他收集调试数据,这种“加载Advice” -》 “卸载Advice”的工作模式具备很好的灵活性。

关于Advice,需要说明的另外一点就是线程安全,因为它加载之后会运行在目标JVM的线程中,目标JVM的方法极有可能是多线程访问的,这也就是说,Advice需要有能力处理多个线程同时访问方法的能力,如果Advice处理不当,则可能会收集到杂乱无章的调试数据。下面的图片展示了Advice和Java-debug-tool调试分析模块、目标方法执行以及调试客户端等模块的关系。

图4-2-2

Advice的首次挂载由Java-debug-tool的命令处理器完成,当一次调试数据收集完成之后,调试数据处理模块会自动卸载Advice,然后进行判断,如果调试数据符合Advice的策略,则直接将数据交由数据处理模块进行处理,否则会清空调试数据,并再次将Advice挂载到目标方法上去,等待下一次调试数据。非首次挂载由调试数据处理模块进行,它借助Advice按需取数据,如果不符合需求,则继续挂载Advice来获取数据,否则对调试数据进行处理并返回给客户端。

4.3 Java-debug-tool的命令设计与实现

4.3.1 命令执行

上文已经完整的描述了Java-debug-tool的设计以及核心技术方案,本小节将详细介绍Java-debug-tool的命令设计与实现。首先需要将一个调试命令的执行流程描述清楚,下面是一张用来表示命令请求处理流程的图片:

图4-3-1

图4-3-1简单的描述了Java-debug-tool的命令处理方式,客户端连接到服务端之后,会进行一些协议解析、协议认证、协议填充等工作,之后将进行命令分发。服务端如果发现客户端的命令不合法,则会立即返回错误信息,否则再进行命令处理。命令处理属于典型的三段式处理,前置命令处理、命令处理以及后置命令处理,同时会对命令处理过程中的异常信息进行捕获处理,三段式处理的好处是命令处理被拆成了多个阶段,多个阶段负责不同的职责。前置命令处理用来做一些命令权限控制的工作,并填充一些类似命令处理开始时间戳等信息,命令处理就是通过字节码增强,挂载Advice进行数据收集,再经过数据处理来产生命令结果的过程,后置处理则用来处理一些连接关闭、字节码解锁等事项。

Java-debug-tool允许客户端设置一个命令执行超时时间,超过这个时间则认为命令没有结果,如果客户端没有设置自己的超时时间,就使用默认的超时时间进行超时控制。Java-debug-tool通过设计了两阶段的超时检测机制来实现命令执行超时功能:首先,第一阶段超时触发,则Java-debug-tool会友好的警告命令处理模块处理时间已经超时,需要立即停止命令执行,这允许命令自己做一些现场清理工作,当然需要命令执行线程自己感知到这种超时警告;当第二阶段超时触发,则Java-debug-tool认为命令必须结束执行,会强行打断命令执行线程。超时机制的目的是为了不让命令执行太长时间,命令如果长时间没有收集到调试数据,则应该停止执行,并思考是否调试了一个错误的方法。当然,超时机制还可以定期清理那些因为未知原因断开连接的客户端持有的调试资源,比如字节码锁。

4.3.4 获取方法执行视图

Java-debug-tool通过下面的信息来向调试者呈现出一次方法执行的视图:

正在调试的方法信息。

方法调用堆栈。

调试耗时,包括对目标JVM造成的STW时间。

方法入参,包括入参的类型及参数值。

方法的执行路径。

代码执行耗时。

局部变量信息。

方法返回结果。

方法抛出的异常。

对象字段值快照。

图4-3-2展示了Java-debug-tool获取到正在运行的方法的执行视图的信息。

图4-3-2

4.4 Java-debug-tool与同类产品对比分析

Java-debug-tool的同类产品主要是greys,其他类似的工具大部分都是基于greys进行的二次开发,所以直接选择greys来和Java-debug-tool进行对比。

5. 总结

本文详细剖析了Java动态调试关键技术的实现细节,并介绍了我们基于Java动态调试技术结合实际故障排查场景进行的一点探索实践;动态调试技术为研发人员进行线上问题排查提供了一种新的思路,我们基于动态调试技术解决了传统断点调试存在的问题,使得可以将断点调试这种技术应用在线上,以线下调试的思维来进行线上调试,提高问题排查效率。
责任编辑人:CC

收藏 人收藏
分享:

评论

相关推荐

11月编程排行榜:Python超过Java成第二

近日,TIOBE公布了 11 月编程指数信息。该榜出现近 20 年来,每月的前两位一直是 C 和 J....
的头像 数据分析与开发 发表于 11-27 16:09 208次 阅读
11月编程排行榜:Python超过Java成第二

准备Java与Python协同开发环境

1.  前言 在本文里,将详细说明如何使用Chaquopy来帮助我们用最简便的方式实现Android Java呼叫Python协同编程。...
发表于 11-24 18:09 101次 阅读
准备Java与Python协同开发环境

Java第二名的位置已经被Python取代了

有人说Python的流行和数据挖掘、人工智能数值计算等领域的蓬勃发展息息相关,但是,TIOBE CE....
的头像 人工智能与大数据技术 发表于 11-23 11:12 321次 阅读
Java第二名的位置已经被Python取代了

怎么样使用嵌套复杂度实现控制流混淆算法的论文资料说明

采用随机插入策略的垃圾代码控制流混淆算法,存在混淆强度和额外开销的不确定性。针对该问题,提出一种基于....
发表于 11-20 17:14 57次 阅读
怎么样使用嵌套复杂度实现控制流混淆算法的论文资料说明

为什么Java中1000==1000为false,而100==100为true呢

为什么 Java 中1000==1000为false,而100==100为true? 这是一个挺有意....
的头像 数据分析与开发 发表于 11-19 15:51 264次 阅读
为什么Java中1000==1000为false,而100==100为true呢

到底怎样才能成长为一名软件教练呢

软件正在改变世界,也在改变着华为。近几年华为招聘了众多业界软件精英,以软件教练身份加入,着力提升自身....
的头像 华为开发者社区 发表于 11-18 10:45 174次 阅读
到底怎样才能成长为一名软件教练呢

面向对象程序设计 - 课内实验1(Java语言概述)

了解 Java的数据类型 掌握各种变量的声明方式。 理解运算符的优先级。 掌握 Java基本数....
发表于 11-17 14:22 37次 阅读
面向对象程序设计 - 课内实验1(Java语言概述)

Python20年来首次超越Java

根据 2020 年 11 月最新出炉的 TIOBE 编程语言排行榜,Python 以 12.12% ....
的头像 DeepTech深科技 发表于 11-16 11:28 396次 阅读
Python20年来首次超越Java

JavaScript是如何获得突出地位的?

JavaScript的卑微起步始于 1995 年,是由当时在 Netscape 通信公司工作的 Br....
的头像 lhl545545 发表于 11-13 09:50 253次 阅读
JavaScript是如何获得突出地位的?

请问Java如何执行cmd命令、bat脚本、linux命令,shell脚本?

Java如何执行cmd命令、bat脚本、linux命令,shell脚本...
发表于 11-10 07:29 0次 阅读
请问Java如何执行cmd命令、bat脚本、linux命令,shell脚本?

最新的编程语言排行说明Python冲上第二

本月的排行榜出现了自 TIOBE 榜单发布以来,近二十年从未见过的变化:前两名的位置首次出现了一个除....
的头像 Wildesbeast 发表于 11-07 10:54 432次 阅读
最新的编程语言排行说明Python冲上第二

Java语言平台版本 及语言特点是什么?

使用计算机的方式有哪些? Java语言平台版本 Java语言的特点是什么? ...
发表于 11-06 07:52 101次 阅读
Java语言平台版本 及语言特点是什么?

Java冒泡排序的原理是什么?

Java冒泡排序的原理
发表于 11-06 07:12 0次 阅读
Java冒泡排序的原理是什么?

Redis 五大数据类型使用场景有哪些

Redis是一种基于键值对的NoSQL数据库,它的值主要由string(字符串),hash(哈希),....
的头像 数据分析与开发 发表于 11-05 17:35 280次 阅读
Redis 五大数据类型使用场景有哪些

10月份GitHub上最热门的Python开源项目上榜详情

10月份GitHub上最热门的Python开源项目排行已经出炉啦,一起来看看上榜详情吧: 1 GHu....
的头像 人工智能与大数据技术 发表于 11-05 15:29 345次 阅读
10月份GitHub上最热门的Python开源项目上榜详情

20年来首次:Java被编程语言 Python超越

日前,TIOBE公布了最新一期的编程语言排行榜(11月)。 接近20年来的首次,Java和C没有分享....
的头像 工程师邓生 发表于 11-05 13:28 391次 阅读
20年来首次:Java被编程语言 Python超越

20年来首次。Java掉出全球最受欢迎的两大编程语言

日前,TIOBE公布了最新一期的编程语言排行榜(11月)。接近20年来的首次,Java和C没有分享前....
的头像 如意 发表于 11-05 12:11 251次 阅读
20年来首次。Java掉出全球最受欢迎的两大编程语言

java有哪几种数据结构?

java单链表结构介绍 java栈结构介绍 java队列结构介绍 ...
发表于 11-05 07:01 0次 阅读
java有哪几种数据结构?

Java程序员应该掌握哪些技术才能实现高薪水?

1-5年开发经验的Java程序员,应该掌握哪些技术才能轻松实现年薪40+w?...
发表于 11-05 06:38 0次 阅读
Java程序员应该掌握哪些技术才能实现高薪水?

java数组那些你不得不知的知识点

数组的概述   数组声明创建   数组使用   稀疏数组的创建以及还原 ...
发表于 11-05 06:00 0次 阅读
java数组那些你不得不知的知识点

Java基本数据类型之间的运算规则是什么?

Java基本数据类型 Java变量的使用说明 Java基本数据类型之间的运算规则 ...
发表于 11-04 09:59 0次 阅读
Java基本数据类型之间的运算规则是什么?

Java变量/标识符的作用及注意事项是什么?

Java变量的作用/使用及注意事项 Java标识符的作用/使用及注意事项 ...
发表于 11-04 06:29 0次 阅读
Java变量/标识符的作用及注意事项是什么?

Java基数据类型有哪些?

Java变量的分类 Java整数的类型 Java字符类型 Java 字符串类型 Java基本数据类型转换 ...
发表于 11-04 06:17 0次 阅读
Java基数据类型有哪些?

怎么样才能检测并发程序中的数据竞争有哪些方法

针对数据竞争检测过程中的误报和漏报问题,提出一种静态数据竞争检测方法。首先,使用控制流分析自动构造线....
发表于 11-03 17:50 54次 阅读
怎么样才能检测并发程序中的数据竞争有哪些方法

为什么说函数是JavaScript的一等公民

总说函数是 JavaScript 的一等公民,很多人就问了,它凭什么? 其实凭的就是对于 JS 这种....
的头像 数据分析与开发 发表于 11-03 09:52 225次 阅读
为什么说函数是JavaScript的一等公民

非计算机专业程序员的经验分享

适逢程序员佳节,来聊聊自己从无到有成为程序员的历程以及自己的经验吧。 懒人目录: 简单历程。 入门。....
的头像 深度学习自然语言处理 发表于 11-02 15:01 344次 阅读
非计算机专业程序员的经验分享

Java会在不久的将来主导编程语言行业

Java是一种通用编程语言,1995年由Sun Micro-systems公司开发。尽管已经有25年....
的头像 如意 发表于 10-28 16:53 384次 阅读
Java会在不久的将来主导编程语言行业

学习Java有前途吗?Java岗位饱和了吗?这篇文告诉你!

现在学Java有前途吗?Java岗位饱和了吗?学Java前途是有的,Java流行的网络编程语言之一,....
的头像 如意 发表于 10-23 16:25 463次 阅读
学习Java有前途吗?Java岗位饱和了吗?这篇文告诉你!

Python受欢迎程度直上升 即将超过Java

来自:程序猿(ID:imkuqin) TIOBE已公布2020年10月的编程语言排行榜。C语言依然排....
的头像 人工智能与大数据技术 发表于 10-23 10:41 452次 阅读
Python受欢迎程度直上升 即将超过Java

SQL和Java的代码写法

根据查询条件查出来的条数越多,性能提升的越明显,在某些情况下,还可以减少联合索引的创建。
的头像 数据分析与开发 发表于 10-21 10:35 325次 阅读
SQL和Java的代码写法

2020年10月编程语言排行榜:Python即将超过Java

来源:菜鸟教程 TIOBE 2020 年 10 月份的编程语言排行榜已经公布,官方的标题是:Pyth....
的头像 inr999 发表于 10-19 11:37 595次 阅读
2020年10月编程语言排行榜:Python即将超过Java

一文知道Java中接口的定义

使用interface来定义一个接口。接口定义同类的定义类似,也是分为接口的声明和接口体,其中接口体....
发表于 10-16 15:44 215次 阅读
一文知道Java中接口的定义

关于Redis缓存的原因及解决方案

下面开始今天的正文,看见小小怎么辛苦的份上,滑到底下,给个素质三连? 缓存雪崩 缓存雪崩是指在某一个....
的头像 39度创意研究所 发表于 10-16 15:22 735次 阅读
关于Redis缓存的原因及解决方案

什么是堆,堆在整个Java集合框架中的作用

堆其实就是一种特殊的队列优先队列。 普通的队列游戏规则很简单:就是先进先出;但这种优先队列 搞特殊 ....
的头像 39度创意研究所 发表于 10-16 11:26 269次 阅读
什么是堆,堆在整个Java集合框架中的作用

基于Java开发的鸿蒙网络访问方面的代码

前言 过了一个漫长的中秋+国庆假期,大家伙的鸿蒙内功修炼的怎么样了?难道像小蒙一样,都在吃吃喝喝中度....
的头像 鸿蒙系统HarmonyOS 发表于 10-16 10:40 601次 阅读
基于Java开发的鸿蒙网络访问方面的代码

JAVA中常见的几个异常类型及处理方案

异常简介 先上个图,看一下常见的几个异常类型。 所有的异常都来自于Throwable。Throwab....
的头像 39度创意研究所 发表于 10-15 16:36 708次 阅读
JAVA中常见的几个异常类型及处理方案

JFinal的源代码资料合集

JFinal 是基于Java 语言的极速 web 开发框架,其核心设计目标是开发迅速、代码量少、学习....
发表于 10-15 08:00 51次 阅读
JFinal的源代码资料合集

盘点Java程序员不能错过的7个基本框架,完美构建复杂应用

现在IT开发人员面对的较大挑战就是复杂性,构建的应用越来越复杂。今天给大家列出Java程序员不能错过....
的头像 如意 发表于 10-14 11:54 458次 阅读
盘点Java程序员不能错过的7个基本框架,完美构建复杂应用

HarmonyOS技术特性及技术架构解析

HarmonyOS是一款面向未来、面向全场景(移动办公、运动健康、社交通信、媒体娱乐等)的分布式操作....
的头像 鸿蒙系统HarmonyOS 发表于 10-13 12:31 855次 阅读
HarmonyOS技术特性及技术架构解析

2020年9月程序员工资最新统计,你了解了吗

链接:https://blog.csdn.net/juwikuang/article/details....
的头像 算法与数据结构 发表于 10-10 17:05 767次 阅读
2020年9月程序员工资最新统计,你了解了吗

甲骨文和谷歌的Java版权之争终迎来结局,安卓能否继续使用Java?

据外媒报道,美国最高法院于当地时间 7 日,开始审理谷歌公司和甲骨文公司有关安卓手机程序中代码版权纠....
的头像 如意 发表于 10-10 09:13 571次 阅读
甲骨文和谷歌的Java版权之争终迎来结局,安卓能否继续使用Java?

啊哈C语言的PDF电子书免费下载

啊哈C语言是一本非常有趣的编程启蒙书,《啊哈C语言》从中小学生的角度来讲述,没有生涩的内容,取而代之....
发表于 10-10 08:00 118次 阅读
啊哈C语言的PDF电子书免费下载

开发人员必知的八个优秀的Java开发工具

Java是计算机应用程序编程语言,被广泛用于创建Web应用、服务器处理、用户端的API开发乃至数据库....
的头像 如意 发表于 10-08 14:18 319次 阅读
开发人员必知的八个优秀的Java开发工具

2020年六大主流编程语言的发展趋势和变化

这个世界上,变化是不可避免的,随着编程语言继续倾向于针对云,微服务,大数据和机器学习中的新趋势进行优....
的头像 如意 发表于 10-08 13:56 683次 阅读
2020年六大主流编程语言的发展趋势和变化

一文详解Java对象的内存布局

这个实例对象是以怎样的形态存在内存中的? 一个Object对象在内存中占用多大? 对象中的属性是如何....
发表于 09-30 14:38 146次 阅读
一文详解Java对象的内存布局

如何在鸿蒙OS上跑起来第一个hello world

咱们一起来跟着一位网友学习一下如何跑起来第一个hello world,原文如下: 前序 1.1 官网....
的头像 电子发烧友网工程师 发表于 09-28 10:55 454次 阅读
如何在鸿蒙OS上跑起来第一个hello world

jvm的类加载器的整体结构及过程解析

前言 我们很多小伙伴平时都是做JAVA开发的,那么作为一名合格的工程师,你是否有仔细的思考过JVM的....
的头像 39度创意研究所 发表于 09-27 15:49 756次 阅读
jvm的类加载器的整体结构及过程解析

2020年Java开发岗位受欢迎的有哪些?这篇文章将告诉你答案

2020年Java开发岗位受欢迎的有哪些?目前计算机专业的大学生想成为Java工程师,参加以实战项目....
的头像 如意 发表于 09-26 10:28 416次 阅读
2020年Java开发岗位受欢迎的有哪些?这篇文章将告诉你答案

Linux系统中JAVA创建文件后权限不足应该如何解决

在作业中,项目使用文件上传。 这个功能很常见。 当Kai Ge今天更改其官方帐户时,他遇到了一个问题....
发表于 09-26 09:06 278次 阅读
Linux系统中JAVA创建文件后权限不足应该如何解决

Danfo.js提供高性能、直观易用的数据结构,支持结构化数据的操作和处理

Danfo.js 是个 JavaScript 开源库,提供了高性能、直观易用的数据结构,支持结构化数....
的头像 TensorFlow 发表于 09-23 18:21 871次 阅读
Danfo.js提供高性能、直观易用的数据结构,支持结构化数据的操作和处理

微软正努力将Java移植到基于ARM的Mac和Windows设备上

和诸多重要开发者一起,微软正努力将Java移植到基于ARM的Mac和Windows设备上,其中就包括....
的头像 如意 发表于 09-23 17:46 428次 阅读
微软正努力将Java移植到基于ARM的Mac和Windows设备上

基于Java JEP数量随着迭代的加速更加容易应对?

在六个月的节奏下,交付可用于生产的 JDK 新版本的速度已大大提高。而不是每隔几年在大型主要版本中发....
的头像 lhl545545 发表于 09-23 11:16 607次 阅读
基于Java JEP数量随着迭代的加速更加容易应对?

阿里巴巴Java开发手册的PDF电子书免费下载

《阿里巴巴 Java 开发手册》是阿里巴巴集团技术团队的集体经验总结,经历了多次大规模一线实战的检验....
发表于 09-21 08:00 77次 阅读
阿里巴巴Java开发手册的PDF电子书免费下载

基于Sti5516芯片的Java虚拟机的软件架构研究

随着现代信息技术的迅猛发展,电视数字化步伐加快了。从一开始的模拟电视到现在的数字电视,不仅大大提高了....
的头像 牵手一起梦 发表于 09-19 17:19 631次 阅读
基于Sti5516芯片的Java虚拟机的软件架构研究

懂高并发性能调优是在技术进阶赛道变得厉害的加分项

懂高并发性能调优,一定是你在技术进阶赛道变得牛逼的加分项。不论,你是开发,架构还是管理岗,亦或者是其....
的头像 算法与数据结构 发表于 09-18 10:39 366次 阅读
懂高并发性能调优是在技术进阶赛道变得厉害的加分项

高宏静Java从入门到精通的PDF电子书免费下载

Java语言是一种跨平台的高级语言,无论是网络世界还是桌面应用程序,无论是分布式应用环境还是嵌入式应....
发表于 09-17 08:00 100次 阅读
高宏静Java从入门到精通的PDF电子书免费下载

TIOBE公布2020年9月编程语言排行 C语言依旧第一

程序猿(ID:imkuqin)编译 TIOBE已公布2020年9月的编程语言排行榜。C语言依然排行第....
的头像 算法与数据结构 发表于 09-15 11:50 1603次 阅读
TIOBE公布2020年9月编程语言排行 C语言依旧第一

基于Jini互联技术实现分布式嵌入式系统的设计

Jini是一种全新的构建分布式系统的技术,具有动态的、自形成的和自管理的特性,它是一种真正的基于服务....
的头像 电子设计 发表于 09-11 17:37 443次 阅读
基于Jini互联技术实现分布式嵌入式系统的设计

Android内存泄漏问题如何优化?

作者:无名之辈FTER 来源:CSDN博客 众所周知,Java因其拥有独特的虚拟机(JVM)设计,使....
的头像 inr999 发表于 09-11 15:55 419次 阅读
Android内存泄漏问题如何优化?

Java的经典面试题和答案详细说明

发现网上很多Java面试题都没有答案,所以花了很长时间搜集整理出来了这套Java面试题大全,希望对大....
发表于 09-07 08:00 60次 阅读
Java的经典面试题和答案详细说明