文章目录
一、什么是JVM、JRE、JDK
1.1 JVM
英文全称Java Virtual Machine,字面意思“Java虚拟机”,简单来说就是个可以运行Java的软件。将.java编译后,会生成相应的.class文件;
.class文件怎么运行?运行在哪里?答案就是JVM。JVM就是加载并运行.class文件的软件。
1.2 JRE
英文全称Java Runtime Enviromental,字面意思“Java运行时的环境”,简单来说就是一个专门运行Java程序的平台。
它包括了JVM和Java的一些常用的类库,JVM就是上面所说的Java虚拟机,而类库就是我们在编写好Java程序后所依赖的核心类和支持文件,
没有这些类库,我们编写好Java程序就没法正常执行,可以说JRE是运行Java程序的最小环境。
1.3 JDK
英文全称Java Development ToolKit,即Java开发工具包,
它除了包括JRE和JVM外,还包括java(用于执行.class文件)、javac(用于将.java文件编译成.class文件)等工具
二、JVM java虚拟机
java之所以能够实现跨平台,便在于其编译阶段不是将代码直接编译为平台相关的机器语言,而是先编译成二进制形式的java字节码,放在Class文件之中,虚拟机再加载Class文件,解析出程序运行所需的内容。每个类都会被编译成一个单独的class文件,内部类也会作为一个独立的类,生成自己的class。
2.1 jvm内存结构
先看一张图
从上图可以看出:
JVM内存结构主要有三大块:堆内存、方法区和栈。堆内存是JVM中最大的一块由新生代和老年代组成,而年轻代内存又被分成三部分,Eden空间、From Survivor空间、To Survivor空间,默认情况下年轻代按照8:1:1的比例来分配;
再来了解一下运行时数据区的几大模块
-
程序计数器
占较小的内存空间,它的作用可以看作是当前线程所执行的字节码的行号指示器 -
Java虚拟机栈
是线程私有的,生命周期和线程相同。
虚拟机栈描述的是java方法(字节码)执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局 部变量表,操作栈,动态链接,方法出口等信息。每个方法被调用直至执行完 成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。 -
本地方法栈
与虚拟机栈作用相似,不同的是本地方法栈是为虚拟机使用的Native方法服务。
-
堆
java虚拟机所管理的内存中最大的一块,用来存放对象实例(实例变量),是被所有线程共享的内存区域,在虚拟机启动时创建。
-
方法区
用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器(JIT)编译后的代码等,存放类变量
方法区和堆是所有线程共享的内存区域;而JVM栈、本地方法栈和程序员计数器是运行时线程私有的内存区域。 -
Java 中静态变量(类变量)和实例变量区别
静态变量属于类,该类不生产对象,通过类名就可以调用静态变量。
实例变量属于该类的对象,必须产生该类对象,才能调用实例变量。
来看JVM和系统调用之间的关系
三、jvm体系总体分为四大块
3.1 类的加载机制
3.2 jvm内存结构
3.3 GC算法 垃圾回收
3.4 GC分析 命令调优
- 什么是类加载器
类的加载器是将类的.class文件中二进制数据读入内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。
类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。
- 类加载器分类
-
启动类加载器
Bootstap ClassLoader,负责加载存放在JDK\jre\lib下(JDK代表JDK的安装目录),或被-Xbootclasspath参数指定的路径中的,并且能被虚拟机识别的类库 -
扩展类加载器
Extension ClassLoader,该加载器由un.misc.Launcher$ExtClassLoader实现,它负责加载JDK\jre\lib\ext目录中,或者由java.ext.dirs系统变量指定的路径中的所有类库(如javax.*开头的类),开发者可以直接使用扩展类加载器。 -
应用程序类加载器
Application ClassLoader,该类加载器由sun.misc.Launcher$AppClassLoader来实现,它负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器 -
自定义类加载器
- 类加载的三种方式
- 命令行启动应用时由JVM初始化加载
- 通过Class.forName()方法动态加载,要求JVM查找并加载指定的类
- 通过ClassLoader.loadClass()方法动态加载,要求JVM查找并加载指定的类
-
Class.forName()和ClassLoader.loadClass()区别
Class.forName():
将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块;Class.forName(name, initialize, loader)带参函数也可控制是否加载static块。并且只有调用了newInstance()方法采用调用构造函数,创建类的对象ClassLoader.loadClass():
只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。 -
类加载机制
-
全盘负责
当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入 -
父类委托
先让父类加载器试图加载该类,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类 -
缓存机制
缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区寻找该Class,只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区。这就是为什么修改了Class后,必须重启JVM,程序的修改才会生效
- 类的生命周期
类的生命周期包括这几个部分,加载、连接、初始化、使用和卸载,其中前三部是类的加载的过程;
- 加载:查找并加载类的二进制数据,在java堆中创建一个java.lang.Class对象
- 连接:包含三块内容:验证、准备、初始化。
- 验证:文件格式、元数据、字节码、符合引用验证;
- 准备:为类的静态变量分配内存,并将其初始化为默认值
- 解析:把类中的符号引用转换为直接引用
- 初始化:为类的静态变量赋予正确的初始值
- 使用:new出对象在程序中使用
- 卸载:执行垃圾回收
四、Question
-
JVM初始化步骤 ?
(1)如果这个类还没有被加载和连接,则程序先加载并连接该类
(2)如果这个类的直接父类还没有被初始化,则先初始化其直接父类
(3)假如类中有初始化语句。则系统依次执行这些初始化语句 -
类初始化时机 ?
只有对类的主动使用的时候才会进行类的初始化
类的主动使用包括以下6种:- 创建类的实例(new Object)
- 访问某个类或接口的静态变量,或者对该静态变量赋值(static关键词)
- 调用类的静态方法
- 反射(如Class.forName(“com.shengsiyuan.Test”))
- 初始化某个类的子类,则其父类也会被初始化
- Java虚拟机启动时被标明为启动类的类(Java Test),直接使用java.exe命令来运行某个主类
-
哪些情况下,Java虚拟机将结束生命周期?
i.执行了System.exit()方法
ii.程序正常执行结束
iii.程序在执行过程中遇到了异常或错误而异常终止
iv.由于操作系统出现错误而导致Java虚拟机进程终止