JDK组成结构
官网地址:https://docs.oracle.com/javase/8/docs/index.html
JDK 在大的层级上可以分为开发工具和 jre 组成,而 jre 则由类库和 jvm 组成。
JDK = 开发工具(Tools & Tool APIs) + JRE
JRE = 类库 + 虚拟机(Java Virtual Machine)
问题:jdk 和 jre 的关联关系是怎样的?
JDK(Java Development Kit)和JRE(Java Runtime Environment)是Java平台的两个主要组件。它们之间的关系如下:
- JDK 包含了 JRE,并且还包含了开发过程中需要的编译器(javac)、调试器、JavaDoc等工具和测试框架等开发工具。
- JRE 只包含了运行Java应用程序所需的组件,如Java虚拟机(JVM)、基础类库、java命令,以及一些需要的配置文件。
简单的说,JDK 就是开发Java程序所需要的开发工具和运行时环境,而JRE只包含了运行Java程序的运行时环境。如果你只需要运行Java程序,那只需要安装JRE就可以了。但如果你需要进行开发,那就需要安装JDK。
另外,需要注意的是,JDK同样也包含了JRE,但JRE不包含JDK。因此,如果你需要使用JDK,就无需单独安装JRE了;如果你只需要运行Java程序,那么只需要安装JRE即可。
总之,Java开发需要安装JDK,而运行Java程序只需要安装JRE即可。
此次重点学习 tools
java
java
命令是 JDK(Java Development Kit)中的一个重要命令,通常我们使用它来启动和运行 Java 应用程序。
命令格式
在 JDK 中,java
命令通常的用法格式如下:
java [options] class [args...]
其中:
options
:是指控制 JVM 行为的命令行选项,例如-server
表示启用服务器性能模式,-Xmx256m
表示将 Java 堆内存最大限制设置为 256MB 等。class
:是指应用程序的主类(Main Class)名称,当 JVM 启动时,它会自动在 classpath 指定的路径中寻找该类的字节码文件,并加载该类的代码。args
:是指运行该 Java 应用程序时所需要的命令行参数,例如我们可以将java MyProgram arg1 arg2
作为启动命令行,在 MyProgram.java 中使用System.out.println(args[0])
和System.out.println(args[1])
来获取到从命令行传递的参数内容。
除了以上的基本语法之外,java
命令还支持一些常用的选项:
-classpath
或-cp
: 为 Java 虚拟机指定类搜索路径,可以是目录、JAR 文件,也可以是类目录和类路径组成的一连串的字符串。例如,在启动 MyProgram 时指定两个 classpath 目录/path/to/classes:/path/to/other/classes
,可以使用java -cp /path/to/classes:/path/to/other/classes MyProgram arg1 arg2
命令行启动 MyProgram 应用程序。-version
:用来显示当前 JVM 的版本信息。-server
:启用服务器模式,在大型应用程序或者需要长时间运行的服务中,这个选项可以提高 JVM 运行效率和吞吐量。-Xmx
或-Xms
:指定 Java 堆的最大内存和初始内存大小。-verbose:class
或-verbose:gc
:开启类加载日志与垃圾回收日志,这些选项可以让我们更好地了解 Java 应用程序的性能状况。-D
:用于在启动时配置 Java 系统属性,可以通过-Dproperty=value
的形式传递多个参数。例如,可以通过-Dmyconfig=/path/to/config.properties
为应用程序传递配置文件的路径。
生产场景及其参数使用
在 JVM 启动 Java 应用程序时,可以使用不同的选项来控制 JVM 的行为,这些选项可以帮助我们优化和调试应用程序,或者解决某些特殊场景下的问题。下面是一些常见的选项以及它们的应用场景:
-
-Xmx<size>
:指定 Java 堆的最大内存大小。例如,-Xmx1g
表示最大内存为 1GB。这个选项可以帮助我们调整 Java 程序的内存占用量,避免 OutOfMemoryError 导致的程序崩溃。 -
-Xms<size>
:指定 Java 堆的初始内存大小。例如,-Xms256m
表示初始内存为 256MB。这个选项可以用于优化 JVM 启动时间,或者调整特定应用程序的内存分配策略。 -
-Xss<size>
:指定线程的最大栈大小。例如,-Xss512k
表示线程栈的大小上限为 512KB。这个选项可以控制线程栈的大小,避免因为线程数太多导致内存溢出。 -
-XX:MaxPermSize=<size>
:指定永久代的最大容量大小。例如,-XX:MaxPermSize=256m
表示最大容量为 256MB。这个选项只对 JDK 7 以前的版本有效,可以用于解决永久代内存溢出等问题。 -
-XX:+UseG1GC
:开启 G1 垃圾回收器,在处理大规模数据和高并发场景时提供更好的性能表现。 -
-XX:+PrintGCDetails
和-XX:+PrintGCDateStamps
:分别用于打印垃圾回收的详细信息和时间戳信息。这些选项可以帮助我们了解垃圾回收的行为和性能状况。 -
-XX:+HeapDumpOnOutOfMemoryError
:在发生 OutOfMemoryError 错误时自动生成堆内存快照。这个选项可以帮助我们快速定位内存泄漏或者程序缓慢等问题。 -
-XX:CompileThreshold=<value>
:指定方法的编译阈值。例如,-XX:CompileThreshold=1000
表示当一个方法被执行 1000 次时,就会被编译成本地代码。这个选项可以优化方法的执行速度,但是可能会导致启动时间延长。
这些选项都是非常常用的,它们可以帮助我们优化和调试 Java 程序。需要注意的是,不同的 JVM 实现和版本可能会支持不完全相同的选项,负责开发运维的同学需要能够熟练掌握这些选项的使用方法,并根据具体情况做出调整。
除了上述提到的常用选项,还有以下几个比较常见的选项:
-
-verbose:class
:开启类加载日志,用于打印类加载器的信息,包括所加载的类的名称、加载时间、来源等。可以用于调试类加载的问题。 -
-D
:用于添加 Java 系统属性,可以通过-Dproperty=value
的形式指定,例如-Djava.net.preferIPv4Stack=true
。在某些场景下,例如解决网络连接问题,需要添加特定的系统属性。 -
-server
:启用服务器模式,会对 JVM 的初始化、编译和垃圾回收等方面做出一些优化,适用于高并发/长时间运行的企业级应用程序。 -
-XX:OnOutOfMemoryError=<cmd>
:在发生 OutOfMemoryError 错误时执行指定的命令,可以用于自动执行垃圾收集或者暂时终止 JVM 等操作。 -
-XX:PermSize=<size>
:指定永久代的初始大小。例如,-XX:PermSize=128m
表示初始大小为 128MB。需要注意的是,在 JDK 8 以后的版本中,永久代已经被元空间(Metaspace)取代,因此该选项已经被废弃。 -
-XX:-UseGCOverheadLimit
:关闭 GC 频繁发生的检查,可以避免在部分系统中因为 GC 太频繁导致程序崩溃的问题。
这些选项都是常用的,可以根据具体需要选择使用。需要注意的是,在使用这些选项时,应该谨慎调整,以防止因为误操作导致程序崩溃或者性能下降。
除了以上列举的常见选项,还有一些比较特殊的选项,可以用于特殊场景下的调试和优化,例如:
-
-XX:+PrintFlagsFinal
:打印 JVM 各种参数的默认值和当前值,用于了解 JVM 配置的具体情况。 -
-XX:+PrintCommandLineFlags
:打印当前 JVM 启动时使用的所有命令行选项,可以用于验证 JVM 配置是否正确。 -
-XX:+UnlockDiagnosticVMOptions
和-XX:+UnlockExperimentalVMOptions
:这两个选项可以解锁一些 JVM 实验性的选项和诊断性的选项,例如启用线程轨迹诊断工具、打印 JVM 内部调试信息等。 -
-XX:+UseSerialGC
:使用串行垃圾收集器,适用于小型单线程应用程序。 -
-XX:+UseConcMarkSweepGC
:使用 CMS 垃圾收集器,适用于高并发大内存应用程序。 -
-XX:+UseParallelGC
:使用并行垃圾收集器,适用于多核多线程的应用程序。 -
-XX:+UseParallelOldGC
:使用并行老年代垃圾收集器,适用于大内存应用程序。
这些选项都是比较特殊的,需要了解具体的使用场景和效果。在使用这些选项时,建议先进行测试和验证,避免因为误操作导致系统不稳定或出现性能问题。
用法: java [-options] class [args...]
(执行类)
或 java [-options] -jar jarfile [args...]
(执行 jar 文件)
其中选项包括:
-d32 使用 32 位数据模型 (如果可用)
-d64 使用 64 位数据模型 (如果可用)
-server 选择 "server" VM
默认 VM 是 server.
-cp <目录和 zip/jar 文件的类搜索路径>
-classpath <目录和 zip/jar 文件的类搜索路径>
用 ; 分隔的目录, JAR 档案
和 ZIP 档案列表, 用于搜索类文件。
-D<名称>=<值>
设置系统属性
-verbose:[class|gc|jni]
启用详细输出
-version 输出产品版本并退出
-version:<值>
警告: 此功能已过时, 将在
未来发行版中删除。
需要指定的版本才能运行
-showversion 输出产品版本并继续
-jre-restrict-search | -no-jre-restrict-search
警告: 此功能已过时, 将在
未来发行版中删除。
在版本搜索中包括/排除用户专用 JRE
-? -help 输出此帮助消息
-X 输出非标准选项的帮助
-ea[:<packagename>...|:<classname>]
-enableassertions[:<packagename>...|:<classname>]
按指定的粒度启用断言
-da[:<packagename>...|:<classname>]
-disableassertions[:<packagename>...|:<classname>]
禁用具有指定粒度的断言
-esa | -enablesystemassertions
启用系统断言
-dsa | -disablesystemassertions
禁用系统断言
-agentlib:<libname>[=<选项>]
加载本机代理库 <libname>, 例如 -agentlib:hprof
另请参阅 -agentlib:jdwp=help 和 -agentlib:hprof=help
-agentpath:<pathname>[=<选项>]
按完整路径名加载本机代理库
-javaagent:<jarpath>[=<选项>]
加载 Java 编程语言代理, 请参阅 java.lang.instrument
-splash:<imagepath>
使用指定的图像显示启动屏幕
有关详细信息, 请参阅 http://www.oracle.com/technetwork/java/javase/documentation/index.html。
更详细的参阅官方文档:https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html
javac
javac
是 JDK 中自带的编译器,是 Java 编程语言的一部分。它可以将 Java 代码编译成字节码,并生成相应的类文件。
1. Java 编译过程
Java 代码的编译过程可以分为三个步骤:编辑、编译和运行。
编辑:使用文本编辑器或者集成开发环境(IDE)撰写 Java 代码。
编译:使用 javac
编译器将 Java 代码编译成字节码(也就是 .class
文件),字节码是运行在 Java 虚拟机(JVM)上的。
运行:使用 java
命令运行已经编译好的字节码文件。
2. Java 编译器的使用方法
javac
编译器通常通过命令行方式使用。下面是一个简单的示例:
javac HelloWorld.java
这个命令会将 HelloWorld.java
文件编译成 HelloWorld.class
文件。
3. 编译器选项
javac
编译器还提供了一些选项,这些选项可以在编译 Java 代码时指定,用于控制编译的行为。
C:\Users\Administrate>javac
用法: javac <options> <source files>
其中, 可能的选项包括:
-g 生成所有调试信息
-g:none 不生成任何调试信息
-g:{lines,vars,source} 只生成某些调试信息
-nowarn 不生成任何警告
-verbose 输出有关编译器正在执行的操作的消息
-deprecation 输出使用已过时的 API 的源位置
-classpath <路径> 指定查找用户类文件和注释处理程序的位置
-cp <路径> 指定查找用户类文件和注释处理程序的位置
-sourcepath <路径> 指定查找输入源文件的位置
-bootclasspath <路径> 覆盖引导类文件的位置
-extdirs <目录> 覆盖所安装扩展的位置
-endorseddirs <目录> 覆盖签名的标准路径的位置
-proc:{none,only} 控制是否执行注释处理和/或编译。
-processor <class1>[,<class2>,<class3>...] 要运行的注释处理程序的名称; 绕过默认的搜索进程
-processorpath <路径> 指定查找注释处理程序的位置
-parameters 生成元数据以用于方法参数的反射
-d <目录> 指定放置生成的类文件的位置
-s <目录> 指定放置生成的源文件的位置
-h <目录> 指定放置生成的本机标头文件的位置
-implicit:{none,class} 指定是否为隐式引用文件生成类文件
-encoding <编码> 指定源文件使用的字符编码
-source <发行版> 提供与指定发行版的源兼容性
-target <发行版> 生成特定 VM 版本的类文件
-profile <配置文件> 请确保使用的 API 在指定的配置文件中可用
-version 版本信息
-help 输出标准选项的提要
-A关键字[=值] 传递给注释处理程序的选项
-X 输出非标准选项的提要
-J<标记> 直接将 <标记> 传递给运行时系统
-Werror 出现警告时终止编译
@<文件名> 从文件读取选项和文件名
例如:
-classpath
选项
该选项用于添加类路径,指定 javac
编译器查找类的位置。例如:
javac -classpath .;lib/* com/example/Main.java
该命令指定了当前目录 .
和 lib
目录下的 jar 文件作为类路径,在编译 Main.java
文件时,需要用到这些类。
-d
选项
该选项指定编译后的 .class
文件输出的目录。例如:
javac -d bin com/example/Main.java
该命令将编译后的 .class
文件输出到 bin
目录中。
-encoding
选项
该选项用于指定源代码文件的字符编码格式。例如:
javac -encoding UTF-8 com/example/Main.java
该命令指定了源代码文件的字符编码格式为 UTF-8。
-source
和-target
选项
这两个选项用于指定编译的 Java 版本和生成的字节码版本。例如:
javac -source 1.8 -target 1.8 com/example/Main.java
该命令指定编译的 Java 版本为 1.8,生成的字节码版本也为 1.8。
-g
选项
该选项用于生成调试信息,以方便在开发过程中使用调试器进行调试。例如:
javac -g com/example/Main.java
该命令表示在编译时生成调试信息。
-nowarn
选项
该选项用于关闭编译器产生的警告信息。例如:
javac -nowarn com/example/Main.java
该命令表示关闭编译器产生的警告信息。
-verbose
选项
该选项用于在编译过程中提供更多的详细信息。例如:
javac -verbose com/example/Main.java
该命令会在编译过程中提供更多的详细信息,以帮助了解编译过程中发生了什么。
这些选项在实际开发中可以根据需要进行组合和使用。例如在实际开发中常用的命令是:
javac -d bin -cp .;lib/* -source 1.8 -target 1.8 -g com/example/Main.java
该命令将源代码文件编译为 .class
文件,并将 .class
文件输出到 bin
目录中。使用 lib
目录下的 jar 文件作为类路径,指定编译时的版本为 1.8,生成的字节码版本也为 1.8,生成调试信息以方便调试。
还有一些常用的选项,比如:
-XDignore.symbol.file=true
选项
该选项用于忽略缺少类文件的错误信息。例如:
javac -XDignore.symbol.file=true com/example/Main.java
该命令会在编译时忽略缺少类文件的错误信息。
-Xlint
选项
该选项用于检测代码中的一些语法和潜在的问题。例如:
javac -Xlint:unchecked com/example/Main.java
该命令会检测未经检查的转换并生成相应的警告信息。
-bootclasspath
选项
该选项用于指定引导类路径,以便编译器可以找到系统类库。例如:
javac -bootclasspath /path/to/jre/lib/rt.jar com/example/Main.java
该命令会在编译时使用指定的 rt.jar
文件作为系统类库。
以上是 javac
常见的选项及其适用场景。在实际开发中,我们可以根据具体的需求和情况,灵活地使用这些选项,以便更好地完成编译任务。
详情可以查看 javac
命令的帮助文档:https://docs.oracle.com/javase/8/docs/technotes/guides/javac/index.html
4. 编译器的原理
javac
编译器的原理是将 Java 代码编译成字节码,字节码是一种与平台无关的中间代码格式,可以在不同的平台上运行。
编译器首先将 Java 代码进行词法和语法分析,生成抽象语法树(AST)。然后进行语义分析,检查是否存在语法错误和类型错误。最后,将 AST 转换为字节码文件。
javac
编译器还支持增量编译,即只对修改过的源代码进行重新编译,提高编译速度。
综上所述,javac
是 JDK 中自带的 Java 编译器,可以将 Java 代码编译成字节码,并生成 .class
文件。javac
编译器还提供了一些选项,用于控制编译的行为,例如 -classpath
、-sourcepath
、-d
选项等。
javac
编译器是 Java 程序的编译器,它将 Java 源文件编译成字节码文件,这些字节码文件可以在 Java 虚拟机(JVM)中运行。在javac
编译 Java 程序时,会经过以下几个主要的编译步骤:
词法分析(Lexical Analysis):将 Java 代码分解成一系列的 Token。
语法分析(Syntax Analysis):将 Token 形成的序列组合成语法树,检测语法错误并生成抽象>语法树(AST)。
语义分析(Semantic Analysis):检查语法树中存在的错误,确认类型、解析符号引用等。
代码生成(Code Generation):将 AST 转换为 Java 字节码文件。
下面分别进行详细解释:
- 词法分析:在
javac
中,主要工作是将源代码的字符流分割成一个个的 Token,这些 Token 是>语言的基本单元,以便后续的处理。例如,对于int x = 1;
语句,javac
将其分解成以下的 Token:[IDENTIFIER:int][IDENTIFIER:x][OPERATOR:=][NUMERIC LITERAL:1][SEMICOLON:;]
- 语法分析:在词法分析后,
javac
将 Token 组合成语法树,主要是对语法结构进行分析和检>>查。如果语法存在错误,例如缺少分号或者括号不匹配等,编译器就会报错并且停止运行。例如,对于下面的语句,javac
将生成以下的语法树:int x = 1;
BlockStatement VariableDeclarationsStatement VariableDeclaration PrimitiveType: int VariableDeclarator Identifier: x NumericLiteral: 1 ;
- 语义分析:在语法分析后,
javac
将检查 AST 中是否存在错误,例如变量的类型是否正确,方法的参数和返回值是否匹配等。此外,javac
还会解析符号引用,确认标识符的含义和定义位置。例如,javac
将检查下面的方法是否存在错误:public void sayHello() { System.out.println("Hello, world!"); }
- 代码生成:在语义分析之后,
javac
将使用上述的语法树和语义信息生成 Java 字节码文件。字节码文件可以在 JVM 中运行,并被转化为机器指令,执行 Java 代码。以上就是
javac
编译器的流程和原理,可以帮助我们深入了解 Java 代码是如何从源代码转换到执行代码的。然而,在实际的开发中,我们可以不必深入了解这些细节,只需要了解常用的编译选项和调试技巧即可。
5. 自定义编译器插件
javac
编译器还支持自定义插件。开发者可以通过编写插件,扩展 javac
编译器的功能,例如实现自定义的语法检查、注解处理、代码生成等功能。
自定义插件需要实现 javax.tools.JavaCompiler
接口。开发者可以使用 ServiceLoader
类来加载自定义插件,并将插件注册到编译器中。
下面是一个简单的示例,展示如何编写一个简单的 javac
插件:
public class SimplePlugin implements CompilerPlugin {
@Override
public String getName() {
return "SimplePlugin";
}
@Override
public void init(JavacTask task, String... args) {
// 初始化插件
}
@Override
public void process(JavacTask task, Set<? extends Kind> kinds,
Iterable<? extends CompilationUnitTree> units,
CompilationUnitTree root) {
// 处理编译过程中的 AST
}
}
上面的 SimplePlugin
类实现了 CompilerPlugin
接口,通过覆盖 getName()
、init()
和 process()
方法来编写插件。init()
方法用于初始化插件,process()
方法用于处理编译过程中的抽象语法树(AST)。
6. 其他扩展
除了自定义插件,javac
编译器还提供了一些其他的扩展机制,注解处理器(Annotation Processor)和编译时注解(Compile-Time Annotation)等。这些扩展机制可以帮助开发者更加便捷地编写和维护 Java 代码。
注解处理器和编译时注解可以用于生成编译器的元数据信息,例如生成配置文件、XML 文件等等。这样做可以减少手动编写配置文件的工作量,同时也可以避免人工错误导致的问题。
综上所述,javac
编译器是 Java 语言的一部分,主要用于将 Java 代码编译成字节码,并生成相应的 .class
文件。它还支持一些扩展机制,例如自定义插件、注解处理器和编译时注解等,可以帮助开发者更好地编写和维护 Java 代码。