java编译究竟是如何进行的?

nas*_*ash 59 java compiler-construction jvm

困惑的java编译过程

好吧我知道这个:我们编写java源代码,平台独立的编译器将其转换为字节码,然后依赖于平台的jvm将其转换为机器代码.

所以从一开始,我们编写java源代码.编译器javac.exe是一个.exe文件.这个.exe文件究竟是什么?是不是用java编写的java编译器,那怎么会有执行它的.exe文件呢?如果编译的代码是java,那么编译器代码是如何在编译阶段执行的,因为它是jvm执行java代码的工作.语言本身如何编译自己的语言代码?这对我来说似乎都是鸡和蛋的问题.

现在.class文件到底包含什么?它是文本形式的抽象语法树,是表格信息,它是什么?

任何人都能告诉我有关如何在机器代码中转换我的java源代码的清晰而详细的方法.

Rek*_*kin 60

好吧我知道这个:我们编写java源代码,平台独立的编译器将其转换为字节码,

实际上,编译器本身工作作为本机的可执行文件(文件javac.exe因此).确实,它将源文件转换为字节码.字节码独立于平台,因为它的目标是Java虚拟机.

然后,依赖于平台的jvm将其转换为机器代码.

不总是.至于Sun的JVM,有两个jvms:客户端和服务器.它们都可以,但不一定必须编译为本机代码.

所以从一开始,我们编写java源代码.编译器javac.exe是一个.exe文件.这个.exe文件究竟是什么?是不是用java编写的java编译器,那怎么会有执行它的.exe文件呢?

exe文件是一个包装的java字节码.这是为了方便 - 避免复杂的批处理脚本.它启动JVM并执行编译器.

如果编译的代码是java,那么编译器代码是如何在编译阶段执行的,因为它是jvm执行java代码的工作.

这正是包装代码的作用.

语言本身如何编译自己的语言代码?这对我来说似乎都是鸡和蛋的问题.

确实,乍看之下令人困惑.虽然,这不仅仅是Java的习语.Ada的编译器也是用Ada编写的.它可能看起来像"鸡和鸡蛋问题",但实际上它只是引导问题.

现在.class文件到底包含什么?它是文本形式的抽象语法树,是表格信息,它是什么?

它不是抽象语法树.AST仅在编译时由tokenizer和编译器用于表示内存中的代码..class文件就像一个程序集,但对于JVM.反过来,JVM是一个可以运行专用机器语言的抽象机器 - 仅针对虚拟机.在最简单的情况下,.class文件具有与正常装配非常相似的结构.在开始时声明所有静态变量,然后是一些外部函数签名表,最后是机器代码.

如果你真的很好奇你可以使用"javap"实用程序挖掘类文件.以下是调用的示例(模糊处理)输出javap -c Main:

0:   new #2; //class SomeObject
3:   dup
4:   invokespecial   #3; //Method SomeObject."<init>":()V
7:   astore_1
8:   aload_1
9:   invokevirtual   #4; //Method SomeObject.doSomething:()V
12:  return
Run Code Online (Sandbox Code Playgroud)

所以你应该知道它到底是什么.

任何人都能告诉我有关如何在机器代码中转换我的java源代码的清晰而详细的方法.

我认为现在应该更清楚,但这里有简短的总结:

  • 您调用javac指向源代码文件.javac 的内部阅读器(或标记器)读取您的文件并从中构建实际的AST.所有语法错误都来自此阶段.

  • javac还没有完成它的工作呢.当它具有AST时,可以开始真正的编译.它使用访问者模式遍历AST并解析外部依赖关系以向代码添加含义(语义).成品保存为.class包含字节码的文件.

  • 现在是时候运行了.您java使用.classfile的名称调用.现在JVM再次启动,但要解释您的代码.JVM可能会也可能不会将您的抽象字节码编译为本机程序集.如果需要,Sun的HotSpot编译器与Just In Time编译一起可以这样做.正在运行的代码由JVM进行分析,并在满足某些规则时重新编译为本机代码.最常见的热门代码是第一个本地编译的代码.

编辑:没有javac人必须使用类似于此的东西调用编译器:

%JDK_HOME%/bin/java.exe -cp:myclasspath com.sun.tools.javac.Main fileToCompile
Run Code Online (Sandbox Code Playgroud)

正如您所看到的那样,它正在调用Sun的私有API,因此它必然会受到Sun JDK的实现.它会使构建系统依赖于它.如果切换到任何其他JDK(wiki列出除Sun之外的5个),则应更新上面的代码以反映更改(因为编译器不太可能驻留在com.sun.tools.javac包中).其他编译器可以用本机代码编写.

所以标准的方法是javac使用JDK 运送包装器.

  • 正确,`javac`只是一个方便的包装.真正的编译器代码在Sun的JDK的`com.sun.tools.javac`包中.你可以去那里看看源代码 - 看到内部结构会很有趣.您也可以直接从Java调用`javac`编译器,而无需调用任何外部进程.如果考虑到这一点,那么`javac`只是隐藏在JDK中的某个类文件的启动器是有道理的.我只能怀疑,但我相信它只是启动JVM并传递参数. (3认同)
  • 所以这是你所描述的场景,我们编写源代码.javac.exe是可执行文件,主要用于收集参数和启动JVM.然后,JVM执行编译器(大量已编译的.class文件的集合)代码.然后编译器执行我们的编写程序.此外,在设计编译器时,还使用其他一些语言(在我们的例子中为C)来为编译器创建.class文件.我说的是对的吗?.exe文件是一个包装的java字节码?它并不完全包装代码.你在谈论什么样的复杂批处理脚本? (2认同)

mat*_*t b 16

是不是用java编写的java编译器,那怎么会有执行它的.exe文件呢?

你从哪里得到这些信息?该javac可执行文件可以写在任何编程语言,它是无关紧要的,所有重要的是,它是一个可执行文件,打开.java文件到.class文件.

有关.class文件的二进制规范的详细信息,您可能会发现Java语言规范中的这些章节很有用(尽管可能有点技术性):

您还可以查看虚拟机规范,其中包括:


Mic*_*rdt 11

编译器javac.exe是一个.exe文件.这个.exe文件究竟是什么?是不是用java编写的java编译器,那怎么会有执行它的.exe文件呢?

Java编译器(至少是Sun/Oracle JDK附带的编译器)确实是用Java编写的.javac.exe它只是一个处理命令行参数的启动程序,其中一些参数传递给运行编译器的JVM,另一些传递给编译器本身.

如果编译的代码是java,那么编译器代码是如何在编译阶段执行的,因为它是jvm执行java代码的工作.语言本身如何编译自己的语言代码?这对我来说似乎都是鸡和蛋的问题.

许多(如果不是大多数)编译器都是用他们编译的语言编写的.显然,在某个早期阶段,编译器本身必须由其他东西编译,但在"bootstrapping"之后,任何新版本的编译器都可以由旧版本编译.

现在.class文件到底包含什么?它是文本形式的抽象语法树,是表格信息,它是什么?

Java虚拟机规范中描述了类文件格式的详细信息.


Mik*_*ron 5

好吧,javac和jvm通常都是原生二进制文件.它们是用C语写的.用Java编写它们当然是可能的,只需要首先需要本机版本.这称为"引导捆绑".

有趣的事实:编译为本机代码的大多数编译器都是用他们自己的语言编写的.但是,它们都必须首先使用另一种语言编写本机版本(通常为C).相比之下,第一个C编译器是用汇编程序编写的.我认为第一个汇编程序是用机器代码编写的.(或者,使用蝴蝶 ;)

.class文件是javac生成的字节码.它们不是文本的,它们是类似于机器代码的二进制代码(但是,具有不同的指令集和架构).

jvm在运行时有两个选项:它可以解释字节代码(假装是CPU本身),也可以JIT(实时)将其编译为本机代码.当然,后者更快,但更复杂.