作者 | 刘欣
责编 | 伍杏玲
本文经授权转载自码农翻身(ID:coderising)
夜已深,但是Java第一代国王却无心睡觉,帝国刚刚树立,东边的C/C++王国凶相毕露,随时预备把重生的王国摧残在摇篮中。
今天GUI大臣上奏,说帝国子民诉苦运转速度慢,这一点Java国王也没有好办法,解说履行嘛,必定比不上编译好的程序,不过Java国王现已指令去研制HotSpot了,比及儿子即位就会大有改观。
这怎样能行?程序不能调试,相当于瘸了一条腿啊!这将严重影响重生Java帝国的找Bug工作。
调试的根底
第二天早朝,眼圈发黑的国王把JVM大臣痛斥了一顿,勒令他马上把调试这一块给搞好。
JVM大臣十分冤枉:“陛下,最初咱们在规划Class文件的字节码的时分,就考虑到了调试的需求,Java文件编译成class文件今后,其中有个叫做LineNumberTable的区域,它描绘了Java源代码和字节码行号(字节码偏移量)之间的对应联系,有了它,咱们才干加断点调试啊!”
他忧虑国王不明白是怎样回事,现场画了一张图。
国王没有心思去了解那些iload, iadd,istore是什么意义,但是他了解了源代码和字节码之间的对应联系,确实是在LineNumberTable中记载的。
源码的第13行 是int sum = x + y;对应的字节码行号是0 ~ 3。
源码中第14行是 return sum。对应的字节码行号是 4 ~ 5。
国王允许认可,问道:“那是不是能够做一个调试器了?”
JVM大臣:“臣正有此意,臣计划把Java的调试器叫做jdb。”
IO大臣听到JDB马上跳了起来:“加(J)多(D)宝(B),你怎样不叫王老吉啊!”
JVM大臣鄙视地看了IO大臣一眼:“C王国有个调试器叫gdb, 我把它叫做jdb, Java Debugger, 别想歪了!”
国王说:“你这个JDB是指令行的吧?”
JVM大臣答道:“陛下明鉴, 臣这儿只能弄个指令行的调试器,由于帝国的子民用的IDE都不相同,臣也无法给每个IDE都开发一个图形界面的调试器,这不是臣应该干的活。”
国王允许:“寡人了解,你的要点仍是要放在HotSpot上,咱们现已被C/C++讪笑很久了,能不能翻身出这口恶气就靠你了。”
GUI大臣说:“陛下圣明,咱们应该充分发挥咱们Java帝国长于拟定规范和协议的专长,搞一套关于调试的规范出来,这样,任何人/任何IDE都能够依据规范来开发一个调试器。”
国王:“爱卿之言甚合我意,GUI大臣,IO大臣,JVM大臣,你们三个通力合作,把这一套规范给拟定出来!”
JVM接口
三位大臣不敢慢待,一退朝就匆促赶到JVM大臣贵寓评论这套规范该怎样拟定。
JVM大臣首先讲话:“诸位,我这儿设置一个底线,那便是调试器和被调试的程序不要处于一个JVM中。”
GUI大臣标明不解:“为什么?”
“很简单,假如它们两个在一个JVM中,那被调试程序的独立性就不能确保了,可能会遭到调试器的影响。举个极点的比如,调试器占有了许多Heap空间,导致被调试程序OOM了.....”
IO大臣:“那咱们能够规划成C/S形式的,让它们之间经过socket通讯怎样样?”
“假如这调试器和被调试程序都在一台机器上,用socket多少有点怪,咱们也要支撑同享内存的办法来通讯。”
GUI大臣说:“如此看来, JVM老兄,你得供给接口啊,让调试器能够拜访Java程序在运转时的状况,嗯,我觉得至少得有这些功用:
获取一个线程的状况, 挂起一个线程,让线程康复履行, 设置一个线程,单步履行
获取线程的当时栈帧,调用栈帧,栈帧对应的办法名
获取变量的值, 设置变量的值
设置断点,铲除断点
检查类的信息,办法,字段 等等”
JVM大臣撇了一眼GUI大臣,心说这家伙是个熟行啊,看来写过不少GUI的调试器,不过他也难不住我,我担任JVM,拿到这些Java程序运转时的信息还不是小菜一碟?
JVM大臣说:“这没问题,我能够把这些接口给细化了,构成规范,然后请一道圣旨,要求各个JVM的供给商都要完成这些接口。”
“不过,” JVM大臣接着说:“为了通用性和功能,我这儿只能供给C言语的接口。嗯,这个接口就叫做JVM Tool Interface,简称JVM TI。”
“那怎样经过socket来运用啊?” GUI大臣急了。
IO大臣说:“封装一下嘛,程序员能够写个程序(Agent),充任通讯的桥梁 。”
通讯
GUI大臣说:“唉,这就麻烦了,咱们还得考虑通讯的协议问题!”
IO大臣:“那是,方才你提的那一大堆调试的需求,都需要能经过网络发给JVM才行,不过不必忧虑,这方面我拿手,让我来拟定一个协议,供调试器和JVM 通讯 !这个协议的称号就叫 (JDWP)Java Debug Wire Protocol 吧。”
IO大臣看到JVM大臣的JVM TI,心中痒痒,也急不可耐地提出了发明了归于自己的缩写。
发明通讯协议的时机可不多,IO大臣浮现出一幅调试器和JVM通讯的场景:
两边先来一个“握手”,标明通讯要开端了,然后调试器能够发送指令给JVM,JVM处理今后发送呼应,还能够主意向调试器推送事情,嗯,这个协议应该是异步的......
调试器
GUI大臣看到这这张图,马上意识到一个问题:“假如咱们把JVM关于调试的才能运用JDWP这个协议的办法露出出来,那调试器能够运用恣意言语来编写啊!”
IO大臣笑道:“是啊,可不仅仅是你老兄的Swing、AWT,他人用C、C++、Python、C#都能够写一个调试器。”
GUI大臣提到:“不不,陛下看到这个规划必定会发怒的,咱们仍是供给一个Java版别的接口吧,让这个接口把JDWP还有什么JVM TI都给封装起来,首要供咱们的Java IDE来运用,来集成。”
看到JVM大臣提出了JVM TI ,IO大臣提出了JDWP,自己没有,怎样在陛下那里交差?GUI大臣赶忙说:“嗯,我期望这个接口叫做 JDI( Java Debug Interface),怎样样?”
三位大臣相视一笑,心照不宣, 这下平衡了。
早朝
又是早朝, JVM大臣代表三人向国王献上了规划图,着重强调了自己提出的JVM TI是多么精妙,完美,至于JDWP、JDI、JVM大臣语焉不详,一笔带过, 气得IO大臣, GUI大臣吹胡子瞪眼。
国王看着规划图,频频允许:“嗯,层次区分得不错,程序员能够直接运用JVM 供给的接口,也能够用JDWP, 还能够用JDI..... ”
三位大臣甚感敬服,国王便是凶猛。
但是国王的脸色很快多云转阴:“只要规划图,代码呢?Talk is cheap , show me the code !”
就在JVM大臣懵逼之时, GUI大臣从怀中掏出一张写满代码的纸,双手呈给了国王,还回过头来对JVM大臣奥秘一笑。
国王拿到了代码,只见上面写着:
创立一个断点:
ClassPrepareEventevent= ....略....
ClassType classType = (ClassType)event.referenceType();
// 获取标明第10行的Location目标
Location location = classType.locationsOfLine(10).get();
// 经过Location目标创立一个断点
BreakpointRequest bpReq = vm.eventRequestManager().createBreakpointRequest(location);
bpReq.enable();
在断点处获取变量的值:
// 抵达了一个断点
BreakpointEventevent= .....略......
//获取当时的线程
ThreadReference threadReference =event.thread();
//获取当时的栈帧
Stackframe stackframe = threadReference.frame();
//从栈帧中得到本地变量 i
LocalVariable localVariable = stackframe.visibleVariableByName("i");
Valuevalue= stackframe.getValue(localVariable);
inti = ((IntegerValue)value).value();
System.out.println("The local variable "+"i"+" is "+ i);
国王扫了一眼,龙颜大悦,提到:“爱卿多虑了,还给我加了这么多注释,其实不加注释我也看得懂,你展现的便是经过JDI这个接口创立断点,然后在断点处获取变量的值。我知道这代码的背面其实会用JDWP协议向JVM TI宣布恳求,由于一切的数据都在那里,对不对?”
JVM大臣赶忙说:“陛下圣明,一会儿就点透了咱们几个小心思。”
“各位爱卿受累了,赏黄马褂,朕计划把你们三个人创立的东西合起来起一个名,叫Java Platform Debugger Architecture,JPDA, 怎样样?”
三人哪敢对立?如小鸡啄米般纷繁允许赞颂,从此, JPDA就成为了Java帝国有关调试的规范,各个IDE逐步都用来起来。
跋文:实际上JDK最早只要 JVM DI (Debugger Interface) 和 JVM PI (Profile Interface),后来才呈现JVM TI,并不是文章中所说的一步到位。
作者简介:刘欣,前IBM架构师,近20年从业经历,「码农翻身」大众号作者,畅销书《码农翻身》作者,用故事解说技能是拿手好戏。拨开技能迷雾,轻松了解技能实质,从「码农翻身」开端。
【END】
热 文推 荐
你点的每个“在看”,我都仔细当成了喜爱