目录
一.JDK、JRE、JVM的关系
1.1JDK(Java SE Development Kit)
1.2JRE( Java Runtime Environment)
1.3JVM(Java Virtual Machine)
1.4JDK、JRE、JVM的区别与联系
二.Class的生命周期
2.1加载
2.1.1 类加载器
2.1.2类加载机制
2.1.3双亲委派
2.2链接
2.2.1验证
2.2.2准备
2.2.3解析
2.3初始化
2.4使用
2.5卸载
三.JVM 内存模型
3.1.运行时数据区
四.垃圾回收机制
一.JDK、JRE、JVM的关系
Java 程序是运行在 JVM(Java 虚拟机)上的,在开发程序之前都要配置 Java 开发环境,其中首先要做的就是 JDK 的安装和配置,那么 JDK、JVM、JRE 到底有何联系和区别呢?想必并不是每一个程序员都能说得清楚的,接下来将带你了解它们之间的关系。
1.1JDK(Java SE Development Kit)
Java标准开发工具包,它提供了编译、运行 Java 程序所需的各种工具和资源,包括 Java 编译器、Java 运行时环境,以及常用的 Java 类库等。下图是 JDK 的安装目录:
1.2JRE( Java Runtime Environment)
Java 运行环境,用于解释执行 Java 的字节码文件。普通用户而只需要安装 JRE就能够Java 程序。而程序开发者必须安装 JDK 来编译、调试程序。下图是 JRE 的安装目录:里面有两个文件夹 bin 和 lib,在这里可以认为 bin 里的就是 jvm,lib 中则是 jvm 工作所需要的类库,而 jvm 和 lib 和起来就称为 jre。
1.3JVM(Java Virtual Machine)
JVM,Java 虚拟机,是 JRE 的一部分。它是整个 java 实现跨平台的最核心的部分,负责解释执行字节码文件,是可运行 java 字节码文件的虚拟计算机。所有平台的上的 JVM 向编译器提供相同的接口,而编译器只需要面向虚拟机,生成虚拟机能识别的代码,然后由虚拟机来解释执行。
当使用 Java 编译器编译 Java 程序时,生成的是与平台无关的字节码,这些字节码只面向 JVM。不同平台的 JVM 都是不同的,但它们都提供了相同的接口。JVM 是Java 程序跨平台的关键部分,只要为不同平台实现了相应的虚拟机,编译后的Java 字节码就可以在该平台上运行
1.4JDK、JRE、JVM的区别与联系
1. JDK 用于开发,JRE 用于运行 java 程序 ;如果只是运行 Java 程序,可以只安装 JRE,无需安装 JDK。
2. JDK 包含 JRE,JDK 和 JRE 中都包含 JVM。
3. JVM 是 java 编程语言的核心并且具有平台独立性,实现了Java语言的跨平台。
二.Class的生命周期
2.1加载
java程序经过javac.exe编译成java.class文件存储在磁盘中,而我们的JVM运行在内存中,所以需要将java.class文件通过IO加载到我们的JVM中。这里就设计到类加载器
2.1.1 类加载器
-
启动类加载器(Bootstrap ClassLoader ):加载$JAVA_HOME 中 jre/lib/rt.jar 里所有的 class 或 Xbootclassoath 选项指定的jar 包! 这些类是 Java 运行的基础类,BootstrapClassLoader 比较特殊,它不继承ClassLoader,而是由 JVM 内部实现;C++实现,并非 java 代码实现
-
扩展类加载器(Extension ClassLoader):加载 java 平台中扩展功能的一些 jar 包,包括$JAVA_HOME 中 jre/lib/ext/*.jar 或-Djava.ext.dirs(启动指令属性)指定目录下的 jar 包
-
系统类加载器(Application ClassLoader):加载 classpath 中指定的 jar 包-Djava.class.path 所指定目录下的类和 jar 包具体实现为 sum.misc.Launcher 中的内部类 AppClassLoader
- 自定类加载器(Custom ClassLoader):通过继承ClassLoader 实现自己的类加载器,可以通过这种方式打破双亲委派机制。
2.1.2类加载机制
-
全盘负责:所谓全盘负责,就是当一个类加载器负责加载某个 Class 时,该Class 所依赖和引用其他 Class 也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入。
-
双亲委派:所谓的双亲委派,则是先让父类加载器试图加载该 Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父加载器,依次递归,如果父加载器可以完成类加载任务,就成功返回;只有父加载器无法完成此加载任务时,才自己去加载。
-
缓存机制。缓存机制将会保证所有加载过的 Class 都会被缓存,当程序中需要使用某个 Class 时,类加载器先从缓存区中搜寻该 Class,只有当缓存区中不存在该 Class 对象时,系统才会读取该类对应的二进制数据,并将其转换成Class 对象,存入缓冲区中。这就是为很么修改了 Class 后,必须重新启动JVM,程序所做的修改才会生效的原因。
2.1.3双亲委派
当一个类收到了类的加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一层的类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载。
采用双亲委派机制的一个好处是比如加载位于rt.jar包中的类java.lang.Object,不管是哪个加载器加载这个类,最终都是委托顶层的启动类加载器进行加载,这样保证了使用不同的类加载器最终得到的都是同一个Object对象。
好处:
- 安全,防止了Java的核心API被随意更改
- 避免了类的重复加载
如何打破双亲委派
自定义类加载器,继承ClassLoder,重写他的loadCLass方法,不让他向上委派。
2.2链接
链接分为三步,分别是验证 - > 准备 - > 解析
2.2.1验证
验证是连接阶段的第一步,这一阶段的目的是为了确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。验证文件格式、元数据、字节码等等
文件格式验证:此阶段保证输入的字节流能正确地解析并存储于方法区之内,格式上符合描述一个 Java 类型信息的要求。
2.2.2准备
为静态变量分配内存,赋值初始值
2.2.3解析
将常量池中的符号引用转为直接引用 解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符 7 类符号引用进行。直接引用就死我们的真实地址。
2.3初始化
为静态变量赋真正的值。到初始化阶段,才真正开始执行类中定义的 Java 程序代码,此阶段是执行 <clinit>() 方法的过程。
2.4使用
就是实例化,new一个类的对像。
2.5卸载
Class 被回收要满足以下三个条件:
a) No Instance:该类所有的实例都已经被 GC;
b) No ClassLoader:加载该类的 ClassLoader 实例已经被 GC;
c) No Reference:该类的 java.lang.Class 对象没有被引用。(XXX.class, 静态变量/方
法)
三.JVM 内存模型
3.1.运行时数据区
方法区:是线程共享的,生命周期和虚拟机一样。存储类信息,静态变量,常量池。方法区满了会OutOfMemoryError(OOM)。
堆:是线程共享的,生命周期和虚拟机一样。存储的是大部分的对象。堆满了也会OOM.堆是GC的主要空间,堆分为老年代和新生代,新生代又分为eden区S0、S1.
默认 老年代:新生代 = 2:1 Eden :S0 : S1 = 8 : 1 : 1
虚拟机栈:是线程私有的,生命周期和创建的线程一样。存储8种基本数据类型和对象的引用,每调用一个方法就会把一个栈帧压栈。栈满了会报错StackOverflowError。线程太多没有足够的空间创建虚拟机栈就会出现OutOfMemoryError(OOM)。
栈帧包括:局部变量表,操作数栈,动态链接,返回地址
操作数栈是用来执行字节码指令过程中用来计算的。
Java虚拟机栈,执行方法的时候,到底经历什么? javap 反编译->字节码指令,对应Java虚拟机栈执行过程
本地方法栈:线程私有的,调用nativ方法的。结构与虚拟机栈相似。
程序计数器:线程私有的,记录程序执行到第几行。他是唯一不会内存溢出的
四.垃圾回收机制
Java垃圾回收机制(GC)
五.JVM参数
1.1标准参数
不会随着 JDK 版本的变化而变化
-version
-help
-server
-cp
1.2-X参数
非标准参数,也就是在 JDK 各个版本中可能会变动
java -Xint -version 解释执行
java -Xcomp -version 第一次使用就编译成本地代码
java -Xmixed -version 混合模式,JVM 自己来决定
1.3-XX参数
使用得最多的参数类型非标准化参数,相对不稳定,主要用于 JVM 调优和 Debug。
a.Boolean 类型
格式:-XX:[+-]<name> +或-表示启用或者禁用 name 属性
比如:-XX:+UseConcMarkSweepGC 表示启用 CMS 类型的垃圾回收器
-XX:+UseG1GC 表示启用 G1 类型的垃圾回收器
b.非 Boolean 类型
格式:-XX:<name>=<value> 表示 name 属性的值是 value
比如:-XX:MaxGCPauseMillis=500
-XX:InitialHeapSize=100M 表示初始化堆大小为100M
1.4其他参数
-Xms100M 等价于-XX:InitialHeapSize=100M
-Xmx100M 等价于-XX:MaxHeapSize=100M
-Xss100k 等价于-XX:ThreadStackSize=100k
java -XX:+PrintFlagsFinal -version 查看参数
1.5五大常用参数
jps 查看java进程
jinfo 实时查看和调整 JVM 配置参数
- 查看:jinfo -flag name PID 查看某个 java 进程的 name 属性的值。 例如:jinfo -flag MaxHeapSize PID
-
修改:参数只有被标记为 manageable 的 flags 可以被实时修改。 jinfo -flag name=value PID。例如: jinfo -flag MaxHeapSize = 20M PID
jstat 查看性能
jstack 查看栈
jmap 查看堆
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heap.hprof 这个参数可以在OOM时自动打印dump文件
jmap -dump:format=b,file=heap.hprof pid 手动打印dump
打印出来的dump文件可以结合工具来分析