查看原文
其他

我是一个Java class

2016-04-05 老刘 码农翻身

前言:本文主要想讲一下Java虚拟机的故事, 可能有点偏门,不妥之处欢迎留言交流。


第一回 陌生警察


我出生在C盘下面一个很深层次的目录下, 也不知道是谁把我放到这里的。

我一直在睡觉,外边的日出日落,风雨雷电和我一点关系都没有。


直到有一天,有个家伙咣咣咣砸我房门把我叫醒。

这个家伙穿着像警察的制服, 左手拿着一个对讲机, 右手递过来他的工作证: "你好, 我是Classloader,  请问你是Account类吗"

"是啊, 怎么了?"

 这个Classloader 没回答我, 反而拿起对讲机:

"头儿,你看看你能不能装载这个Account类?” 

对讲机那头好像也在问他的上司,过了半天,终于有了回音:

"我装载不了, 我的上级也说了,他们也装载不了, 你来干吧"


"那就报数吧~” 我这次注意到旁边站着另外一个笑眯眯的小个子。

"报什么数?" 我一脸诧异。

"唉,果然没有被装载过, 你是个class 文件,当然要报文件开头的那几个数了, 就是Java 他爸James Gosling 在jdk 1.0时确定的那个数啊"

"奥, 我看看, 0xCAFEBABE"

"不错, 是个java 类, 把你后边的两个数也报一下", 小个子继续问

"50 , 0" 

"看来版本不高啊, 是jdk 1.6编译出来的啊",  小个子接着说 "最新的虚拟机都1.8了,  都函数式了,你造不?”

我哪里知道? 我这才模模糊糊的回想起来, 好像是有个什么javac 把我创建出来,扔到了这个屋子里。


"现在奉命带你去Java 虚拟机, 有人需要你的帮助" , 这个Classloader 态度冷冰冰的, 我不喜欢他。 

"大哥,你们咋找到我的?"  我决定和小个子套近乎。

"那还不简单, 我们老板有个列表, 上面列举着所有应该检查的目录,我们顺藤摸瓜,一个一个找,肯定能找到"

"那万一找不到咋办?"

"基本不可能, 你看老板给我们的目录列表中有 C:\workspace\myTaobao\bin  , 我们在下面再找三级 com/mytaobao/domain, 这不就找到你了吗, 

 Account.class , 话说回来, 万一真找不到, 将来在执行时会抛出ClassNotFound异常了, 那不归我们管"


 我后来才知道, 我的全名其实叫做com.mytaobao.domain.Account !


"来来来, 让我验证一下, 你这class编译的对不对" ,小个子拿出一个放大镜

"恩, 常量池,  访问标识, 字段,方法... 看起来没有问题“ , 小个子对Classloader说。 

被人拿着放大镜看,这种感觉极为不爽。


"走, 去虚拟机"  , Classloader还是冷冰冰的。 

这哥俩不容我带任何东西, 便把我推上车,飞奔向我没听说过的“虚拟机”。 


第二回 刺探信息


我感到前途未卜, 但也不能坐以待毙, 一定得多了解信息。

"大哥, 你叫什么名字" , 我看小个子还算和气。

"我就是大名鼎鼎的文件验证器了, 能管很多事"

"那刚才他为啥还得请示上级呢" , 我用眼神指了一下开车的ClassLoader

文件验证器的声音一下子就压低了:

"你不知道,说来话长, 我们之前出现过事故,有个黑客写了个类java.lang.String,  和我们老板手下有一个干活最卖力的员工名字一模一样,只是这个黑客类里边竟然有格式化硬盘的代码,我们的小兵Classloader 不明就里,就把这个黑客类给先装载了,也执行了, 最后的结果,唉,很惨的... "


"那后来怎么办?" 

"后来我们老板就定下了规矩:他的骨干员工像String, ArrayList等只能由他自己的心腹去装载, 我听说老板的心腹都是分层级的,像传销一样, 每个都有上线, 最顶层的叫Bootstrap Classloader , 下一次级叫Extension Classloader, 现在开车的这位其实叫App Classloader,位于最底层,  咱这位Classloader 在装载一个类之前,一定要问一问这几位权利极高的大爷,请他们先装载,这几位爷装载不了,才由我们这些小兵来出马。“


"这能避免黑客攻击?"

"能啊! 你想想, 那个黑客写了个攻击的java.lang.String,  我们在装载之前,肯定要请示Extension, Bootstrap这些大爷先来装载, 由于String是老板的核心员工,肯定会他们先装载啊, 这些大爷把String 直接就给我们了, 我们就不会装载黑客类了"


“你能不能少说两句” Classloader 似乎生气了。

我和文件验证器只好禁声。


其实文件验证器也不是只会给我吹牛, 他也很敬业, 这家伙在车上把我全部的字节码都要了过去, 对这些天书一般的东西一遍一遍的检查分析,确保每个指令都是正确的, 检查是不是有超类, 是不是覆盖了final方法,跳转指令是不是正确....


第三回 初识虚拟机


很快我们就来到了目的地, 我一看虚拟机不就是几个大楼嘛, 不过这几座大楼可真是高啊。


他俩把我带进其中一座叫“方法区”的大楼,进了电梯, 输入2048 。 

很快来到第2048层, 无数的格子间平铺开来,他们七拐八拐,轻松的把我带到了我的位置, 上面写着我的名字“com.mytaobao.domain.Account”.


我问文件验证器: “这楼这么高, 这么多格子间, 人会坐满吗?”

"只有极少情况会坐满, 一旦满了,那时候会抛出异常, 我们就完蛋了。 你自己好自为之吧, 再见 "

他们把我安顿好就立刻离开了。 


我往周边一看, 咦,这不是著名的java.lang.String吗。

我本想和他打个招呼, 可以他的电话似乎一直没断过, 嘴里一直说着什么store, load之类我听不懂但是似乎有点熟悉的话。 

正无聊着呢,我桌子上的电话也响了, 电脑屏幕也亮了,我看到一个人对我笑着说:

"你好, 我刚刚new 出来的Account对象, 我的编号是"


晕倒 ! 这家伙和我什么关系? 

看我一脸的诧异, 他说,“ 很快就会有个线程到CPU车间了,他会联系你, 我就是想确认下你在不在,  奥对了, 我在一个叫做堆的地方, 有空找我玩啊, byebye ”, 说完就消失了。


果然没多久, 视频电话又响了。

这次我看到一个人站在一个明亮的车间里, 抱着一个包裹, 他按了一个按钮, 面前立刻升起一个工作台 , 台子上立了一个有很多抽屉的柜子,每个抽屉上都有一个编号, 旁边还有一个深桶。 

(后来我就知道, 那个柜子的学名叫做局部变量区 , 那个桶叫做 操作数栈


我正想问问问怎么回事呢, 就听到了他的声音:

"我是线程0x3704,  我要调用你第二个方法了“  

(码农翻身注: 不认识线程0x3704的同学可以回复“我是一个线程”查看)


我一看, 我的第二个方法是add :

public void add(int x , int y ){

    x = x + y;

    .....其他代码略....

}       

(码农翻身注: Account类当然看不到这些源码, 这是为了方便你看的  :-) )


"请把第一条指令给我说一下"    0x3704 继续问我要东西

我还不太熟练,找了半天才说:

 "iload_0" 

于是他就操作柜子上的机械手把0号抽屉的一个数30扔到到了工作台上的一个桶里,这个桶很窄,没法并排放两个数, 但是很深。



 然后0x3704说 “下一条指令”

"iload_1" 

于是1号抽屉的一个数40也被扔到了桶里,正好压在30上面, 从桶上面就看不到30了。



“下一条指令”

”iadd“

于是他就把两个数从桶里取了出来, 做了个飞快的动作, 这两个数变成了一个数 70 !, 然后他又把70 放到了桶里。


 “下一条指令”

"istore_0"

于是他把70从桶里捞出来, 放到了柜子上编号为0的地方, 之前的30就被扔掉了。

我看的目瞪口呆,这厮是在干嘛???


我问他: “0x3704, 不就是把两个数加起来吗? 为啥搞的这么麻烦”

他不理我, 只是继续说, “下一条指令”

我只有配合它玩这个游戏。


java.lang.String 难得的悠闲, 端着一杯咖啡一边看我手忙脚乱的取指令, 一边说:

"新人都这样, 别着急,等你熟练了,闭着眼睛就搞定了, 就像我一样,你可能不知道 , 我们这个虚拟机叫做基于堆栈的虚拟机, 看到那个桶没有,其实就是个先进后出的栈啊, 我们虚拟机的所有指令其实都是在对栈进行操作"


可是我还是好奇: “这栈有什么好啊”

旁边的格子间的java.util.Stack 立刻说:

"这事儿你得问我啊, 怎么说呢, 主要是为了简单, 你看我们只用一个简单的桶,奥对了,栈, 就能完成所有的工作, 你做要的就是往栈里扔东西(入栈), 然后从最上面拿东西(出栈) 就行了。 不像intel 的CPU, 搞了巨多的桶,每个桶只能容纳一个数, 他们还美名其曰寄存器, 做加法的时候, 先把一个数放到第一个桶, 再把另外一个数放到第二个桶,加起来以后的结果还得找个桶,有些桶还不通用,这么多桶找起来麻烦死了。 "


"可是我们的栈操作起来就麻烦了啊, 你看一个简单的加法都得操作半天"  ,我不依不饶。

"我们的指令可以优化啊, 不过这我也不太懂"


这个游戏我整整完了一天,没有线程找我的时候, 我就闲着, String说得对, 熟练以后简直太简单了。


String 就不一样了, 几乎每时每刻都线程给他打电话要指令, 这么没办法, String确实是虚拟机的骨干和精英, 使用频繁,业务纯熟,忙而不乱。


有时候我会看到线程有不止一个工作台, 而是一摞子工作台, 也是一个压一个, 线程们都很老实,永远在最上面那个工作, 从来不会先干下面的活。



我问java.util.Stack :"这些工作台也是栈吧"


"猜的不错,学名叫Java 栈,每个线程都有一个,  其中的每个工作台你看过了 ,学名叫栈帧, 知道不?  每个台子都代表一个方法调用, 这一摞工作台就方法调用方法导致的啊 "


确实是, 因为我发现一旦调用新方法, 立刻就会形成一个新的工作台, 压在老的上面。 方法调用完成后, 栈顶的工作台就被销毁了, 线程会在底下的工作台继续机械的干活。  


第四回 快乐假期

第二天, 0x3704又问我要指令, 我有点生气: 你就不会记住吗

0x3704说: 我可不能记住, 万一你被重新装载了, 指令变了怎么办? 


我告诉他指令是"iload_0" , 他刚把数据扔到桶里,  古怪的事情发生了, 身手敏捷的0x3704突然好像凝固了一样,不动了。 


只听到String欢呼: “遇到断点了,码农开始调试了, 我们放假了!”

"调试?什么调试?" 

"就是码农会单步、手工的执行这些指令,他们慢死了, 可能一秒才能执行一步, 由于我们的时间比他们快的多, 他们的一秒,简直就是我们的10几天, 走, 出去玩去"


"出去玩? 能上哪儿玩” 我觉得这里无聊透顶。

"找我们new 出来的对象玩去"

我想到了之前联系过我的 对象 , 想着去看看也不错。


这个叫"堆"的大楼更加拥挤, 全是人, String 的对象当然最多,Stirng类左右逢源,不停的打招呼, 从我创建出来的Account对象几乎找不到。


一队全副武装的士兵不停的在巡逻, 时不时的把对象拉出来,塞到车里去。


“这是在干嘛啊”  我问String类

"这些人叫清理者, 专门清理没有用的对象, 你看,车里那不是 吗"

"啊? 昨天我还和他联系, 他怎么会没用了呢"

"他很有可能只是个方法的局部变量, 方法结束后, 就没人引用了, 白白的占用空间, 你看这楼太拥挤了, 如果不清理, 很快就会住满,系统崩溃, Out Of Memory了"


"那这个楼就不能盖的更高点吗?” 我心里有点可怜这些被回收的对象们

"楼有多高,是由码农们决定的, 他们在启动虚拟机的时候会指定参数"


"那士兵咋知道谁有用没用?"

"引用计数呗, 如果对象被使用, 计数就会增加, 不用的时候就会减少, 如果是0 , 那就可能被清理了。"


"那我们会被清理掉吗?" 我担心的问

String类神秘的笑了下:  "我应该不会, 但是你是有可能的" 

我当然明白了, String类是核心员工,  而我只是从外边加载过来的一个类而已, 不过我也确实有点想我的家了。 


果然,又过了10天, 0x3704才动弹了一下,问我要第二条指令 

我想都没想就告诉了他:“iload_1” 。 

接下来又是10天的长假。 


第五回 真相大白

漫长的调试假期终于结束了,我刚回到自己的工作间,  发生了更奇怪的事情, 整个世界毫无征兆的消失了。 


我晕晕乎乎,发现还是躺在自家床上, 我是做了一场梦吗? 

可是过去的记忆如此的真切, 到底是怎么回事?


管它呢, 我已经知道了自己所在的房子的门牌号是 C:\workspace\myTaobao\bin\com\mytaobao\domain

探索一下吧,唉 , 大部分人都非常无趣,不理我。


正当我准备要回去接着睡觉的时候, 我先发现了C:\workspace\myTaobao\src\ 下也有个一模一样的目录com\mytaobao\domain,关键是里边竟然有个Account.java !

出生的模糊记忆告诉我, javac 就是从这里把我生成的。 


我正要给他打招呼,一个"hi"还没说出口。 

javac 又一次运行, 我被新的Account.class 残忍的覆盖掉了! 


临死前, 我终于明白了,这个一个码农的电脑,码农在开发程序, 调试程序, 不断的重启服务器。 

而我这个类隐藏着一个Bug, 经过调试后被发现, 然后Fix了!

(全文完)


特别感谢: 网友blindingdark 提供的形神具备的配图, 这可是先在纸上手绘, 然后扫描变成图片, 最后用画笔涂改出来的啊 :-)


声明:原创文章,版权所有, 未经授权,不得转载。


----------------------------------------------------------------

“码农翻身” 公共号 : 由工作15年的前IBM架构师创建,分享编程和职场的经验教训。

长按二维码, 关注码农翻身


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存