java中计算一个文件的总字节数_【JVM故事】一个Java字节码文件的诞生记

作者:李新杰·转自微:信公众号“编程新说”

万字长文,完全虚构。(12000字)

(一)

组里来了个实习生,李大胖面完之后,觉得水平一般,但还是留了下来,为什么呢?各自猜去吧。

李大胖也在心里开导自己,学生嘛,不能要求太高,只要肯上进,慢慢来。就称呼为小白吧。

小白每天来的很早,走的很晚,都在用功学习,时不时也向别人请教。只是好像天资差了点。

都快一周了,才能写些“简单”的代码,一个注解,一个接口,一个类,都来看看吧:

public @interface Health {

String name() default "";

}

public interface Fruit {

String getName();

void setName(String name);

int getColor();

void setColor(int color);

}

@Health(name = "健康水果")

public class Apple implements Fruit {

private String name;

private int color;

private double weight = 0.5;

@Override

public String getName() {

return name;

}

@Override

public void setName(String name) {

this.name = name;

}

@Override

public int getColor() {

return color;

}

@Override

public void setColor(int color) {

this.color = color;

}

public double weight() {

return weight;

}

public void weight(double weight) {

this.weight = weight;

}

}

与周围人比起来,小白进步很慢,也许是自己不够聪明,也许是自己不适合干这个,小白好像有点动摇了。

这几天,小白明显没有一开始那么上进了,似乎有点想放弃,这不,趴在桌子上竟然睡着了。

(二)

在梦中,小白来到一个奇怪又略显阴森的地方,眼前有一个破旧的小房子,从残缺不全的门缝里折射出几束光线。

小白有些害怕,但还是镇定了下,深呼吸几口,径直朝着小房子走去。

小白推开门,屋里没有人。只有一个“机器”在桌子旁大口大口“吃着”东西,背后也不时的“拉出”一些东西。

小白很好奇,就凑了上去,准备仔细打量一番。

“你要干嘛,别影响我工作”。突然冒出一句话,把小白吓了一大跳,慌忙后退三步,妈呀,心都快蹦出来了。

“你是谁呀?”,惊慌中小白说了句话。

“我是编译器”,哦,原来这个机器还会说话,小白这才缓了过来。

“编译器”,小白好像听说过,但一时又想不起,于是猜测到。

“网上评论留言里说的小编是不是就是你啊”?

“你才是呢”,编译器白了一眼,没好声气的说到。

要不是看在长得还行的份上,早就把你赶走了,编译器心想。

“哦,我想起来了,编译器嘛,就是编译代码的那个东西”,小白恍然大悟到。

“请注意你的言词,我不是个东西,哦,不对,我是个东西,哦,好像也不对,我。。我。。”,编译器自己也快晕了。

编译器一脸的无奈,遇上这样的人,今天我认栽了。

小白才不管呢,心想,今天我竟然见到了编译器,我得好好请教请教他。

那编译器会帮助她吗?

(三)

小白再次走上前来,定睛一看,才看清楚,编译器吃的是Java源码,拉的是class(字节码)文件。

咦,为啥这个代码这么熟悉呢,不就是我刚刚写的那些。“停,停,快停下来了”。编译器被小白叫停了。

“你又要干嘛啊”?编译器到。

“嘻嘻,这个代码是我写的,我想看看它是怎么被编译的”,小白到。

编译器看了看这个代码,这么“简单”,她绝对是个菜鸟。哎,算了,还是让她看看吧。

不过编译器又到,“整个编译过程是非常复杂的,想要搞清楚里面的门道是不可能的,今天也就只能看个热闹了”。

“编译后的内容都是二进制数据,再通俗点说,就是一个长长的字节数组(byte[])”,编译器继续说,“通常把它写入文件,就是class文件了”。

“但这不是必须的,也可以通过网络传到其它地方,或者保存在内存中,用完之后就丢弃”。

“哇,还可以这样”,小白有些惊讶。编译器心想,你是山沟里出来的,没见过世面,大惊小怪。

继续到,“从数据结构上讲,数组就是一段连续的空间,是‘没有结构’的,就像一个线段一样,唯一能做的就是按索引访问”。

小白到,“编译后的内容一定很繁多,都放到一个数组里面,怎么知道什么东西都在哪呢?不都乱套了嘛”。

编译器觉得小白慢慢上道了,心里有一丝安慰,至少自己的讲解不会完全白费。于是继续到。

“所以JVM的那些大牛们早就设计好了字节码的格式,而且还把它们放入到了一个字节数组里面”。

小白很好奇到,“那是怎么实现的呢”?

“其实也没有太高深的内容,既然数组是按位置的,那就规定好所有内容的先后顺序,一个接一个往数组里放呗”。

“如果内容的长度是固定(即定长)的,那最简单,直接放入即可”。

“如果内容长度是不固定(即变长)的,也很简单,在内容前用一到两个字节存一下内容的长度不就OK了”。

(四)

“字节码的前4个字节必须是一个固定的数字,它的十进制是3405691582,大部分人更熟悉的是它的十六进制,0xCAFEBABE”。

“通常称之为魔术数字(Magic),它主要是用来区分文件类型的”,编译器到。

“扩展名(俗称后缀名)不是用来区分文件类型的吗”?小白说到,“如.java是Java文件,.class是字节码文件”。

“扩展名确实可以区分,但大部分是给操作系统用的,或给人看到。如我们看到.mp3时知道是音频、.mp4是知道是视频、.txt是文本文件”。

“操作系统可以用扩展名来关联打开它的软件,比如.docx就会用word来打开,而不会用文本文件”。编译器继续到。

“还有一个问题就是扩展名可以很容易被修改,比如把一个.java手动改为.class,此时让JVM来加载这个假的class文件会怎样呢”?

“那JVM先读取开头4个字节,发现它不是刚刚提到的那个魔数,说明它不是合法的class文件,就直接抛异常呗”,小白说到。

“很好,真是孺子可教”,编译器说道,“不过还有一个问题,不知你是否注意到?4个字节对应Java的int类型,int类型的最大值是2147483647”。

“但是魔数的值已经超过了int的最大值,那怎么放得下呢,难道不会溢出吗”?

“确实啊,我怎么没发现呢,那它到底是怎么放的呢”?小白到。

“其实说穿了不值得一提,JVM是把它当作无符号数对待的。而Java是作为有符号数对待的。无符号数的最大值基本上是有符号数最大值的两倍”。

“接下来的4个字节是版本号,不同版本的字节码格式可能会略有差异,其次在运行时会校验,如JDK8编译后的字节码是不能放到JDK7上运行的”。

“这4个字节中的前2个是次(minor)版本,后2个是主(major)版本”。编译器继续到,“比如我现在用的JDK版本是1.8.0_211,那次版本就是0,主版本就是52”。

“所以前8个字节的内容是,0xCAFEBABE,0,52,它们并不是源代码里的内容”。

Magic [getMagic()=0xcafebabe]

MinorVersion [getVersion()=0]

MajorVersion [getVersion()=52]

(五)

当编译器读到源码中的public class的时候,然后就就去查看一个表格,如下图:

自顾自的说着,“public对应的是ACC_PUBLIC,值为0x0001,class默认就是,然后又读ACC_SUPER的值0x0020”。

“最后把它俩合起来(按位或操作),0x0001 | 0x0020 => 0x0021,然后把这个值存起来,这就是这个类的访问控制标志”。

小白这次算是开了眼界了,只是还有一事不明,“这个ACC_SUPER是个什么鬼”?

编译器解释到,“这是历史遗留问题,它原本表达在调用父类方法时会特殊处理,不过现在已经不再管它了,直接忽略”。

接着读到了Apple,它是类名。编译器首先要获取类的全名,org.cnt.java.Apple。

<
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值