1.JVM是什么
JVM全称为 Java Virual Machine(Java 虚拟机)
所谓的虚拟机,就是一台虚拟计算机,大体上虚拟计算机分为系统虚拟机和程序虚拟机
2.为什么要学JVM
作为一个Java工程师,是否遇到过以下问题:
1.运行着线上的系统突然卡死,系统无法访问,甚至直接OOM
2.想解决线上的JVM GC问题却无从下手
3.新项目上线,对各种JVM参数一脸懵逼,直接默认吧然后就GG了
4.每次面试前都要重新背一遍JVM的一些概念性东西。然而面试官在问你JVM参数、调优时直接一脸懵逼
所以针对上述问题,JVM是必学的一项知识点
1.面试的需要(BAT等大厂面试必问!)
2. 中高程序员的必备技能
3.追求极客精神(垃圾回收算法、JIT 即时编译器、底层原理)
1.Java的跨平台性
字节码
我们平时说的字节码文件,是由我们编写的java文件,通过jdk编译后转换成对应的字节码文件。通过jvm运行字节码文件后再对底层的操作系统作交互
2.历史上Java的重大事件
1990年,在Sun计算机公司中,由Patrick Naughton、MikeSheridan 及James Gosling领导的小组Green Team,开发出的新的程序语言命名为Oak,后期命名为Java
2004年,JDK1.5发布。同时JDK1.5改名为JavaSE5.0。
2010年,Oracle收购了Sun,获得Java商标和最真价值的HotSpot虚拟机此时,Oracle拥有市场占用率最高的两款虚拟机HotSpot和JRockit 并计划在未来对它们进行整合:HotRockit
3.JVM介绍
虚拟机概念
所谓的虚拟机(VM),就是一台虚拟的计算机 它是一款软件,用来执行一系列虚拟计算机指令
大体上虚拟机可以分为系统虚拟机和程序虚拟机
1.大名鼎鼎的Virtual Box,VMware就属于系统虚拟机。它们完全是对物理计算机 的仿真,提供了一个可运行完整操作系统的软件平台
2.程序虚拟机的典型代表就是Java虚拟机,它专门为执行单个计算机程序而设计。在Java虚拟机中执行的指令我们称为Java字节码指令
3.无论是系统虚拟机还是程序虚拟机 在上面运行的软件都被限制于虚拟机提供的资源中
Java虚拟机
1.Java虚拟机是一台执行Java字节码的虚拟计算机
它拥有独立的运行机制,其运行的Java字节码也未必由Java语言编译而成
2.JVM平台的各种语言可以共享Java虚拟机带来的跨平台性、优秀的垃圾回收,以及可靠的及时编译器
3.Java技术的核心就是Java虚拟机(JVM,Java Virtual Machine) 因为所有的Java程序都运行在Java虚拟机内部
4.Java虚拟机就是二进制字节码的运行环境,负责装载字节码到其内部 解释/编译为对应平台上的机器指令执行 每一条Java指令,Java虚拟机规范中都有详细定义,如怎么取操作数 怎么处理操作数,处理结果放在哪里
5.特点
5.1 一次编译,处处运行
5.2 自动内存管理
5.3 自动垃圾回收功能
JVM架构简图
类加载子系统
JVM架构图
类加载子系统的作用
1.类加载子系统负责从文件系统或者网络中加载class文件,class文件在文件开头有特定的文件标识(cafe babe),也就是我们提到的"魔数"
2.ClassLoader只负责class文件的加载,至于它是否可以运行,则由 Execution Engine决定
3.加载类信息存放于一块成为方法区的内存空间,除了类信息外, 方法区还会存放运行时常量池信息\
可能还包括字符串字面量和数字常量,这部分常量信息是Class文件中常量池部分的内存映射
ClassLoader-加载
1.通过一个类的全限定制获取定义此类的二进制字节流
2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据
3.在内存中生成一个代表这个类的Class对象,作为方法区这个类的各种数据的访问入口
ClassLoader-链接
1.验证: 目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求保证被加载类的正确性,不会危害虚拟机自身安全主要包括四种验证,文件格式验证,源数据验证,字节码验证,符号引用验证
2.准备:为类变量分配内存并且设置该类变量的默认初始值,即零值; 这里不包含用final修饰的static,因为final在编译的时候就会分配了准备阶段会显示初始化; 类不会为实例变量分配初始化,类变量会分配方法区中,而实例变量是会随着对象一起分配到java堆中
3.解析:将常量池内的符号引用转换为直接引用的过程事实上,解析操作完会伴随着jvm在执行完初始化之后再执行符号引用就是一组符号来描述所引用的目标符号应用的字面量形式明确定义在《java虚拟机规范》的class文件格式中直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄
ClassLoader-初始化
执行类构造器方法clinit()的过程此方法不需要定义
是javac编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来
(如果没有静态变量,那么字节码文件中就不会有clinit方法) 构造器方法中指令按语句在源文件中出现的顺序执行 clinit()不同于类的构造器。(关联:构造器是虚拟机视角下的init())若该类具有父类,jvm会保证子类的clinit()执行前,父类的clinit()已经执行虚拟机必须保证一个类的clinit()方法在多线程下被同步加锁完毕
类加载器
JVM支持两种类型的加载器
分别是 引导类加载器(BootStrap ClassLoader) 和 自定义类加载器(User-Defined ClassLoader)
从概念上来讲自定义类加载器一般指的是程序中由开发人员自定义的一类类加载器
但是java虚拟机规范却没有这么定义,而是将所有派生于抽象类ClassLoader的类加载器划分为自定义类加载器
自定义类与核心类库的类加载器
public static void main(String[] args) {
// 获取系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
// sun.misc.Launcher$AppClassLoader@18b4aac2
System.out.println(systemClassLoader);
// 获取系统类加载器的上级加载器 -> 拓展类加载器
ClassLoader extClassLoader = systemClassLoader.getParent();
// sun.misc.Launcher$ExtClassLoader@31cefde0
System.out.println(extClassLoader);
// 获取拓展类加载器的上级加载器 -> 引导类加载器
ClassLoader bootstrapClassLoader = extClassLoader.getParent();
// null
System.out.println(bootstrapClassLoader);
// 获取本类的类加载器
ClassLoader classLoader = this.getClass().getClassLoader();
// sun.misc.Launcher$AppClassLoader@18b4aac2
System.out.println(classLoader);
// 获取核心类库中String的类加载器
ClassLoader stringClassLoader = String.class.getClassLoader();
// null
System.out.println(stringClassLoader);
}
虚拟机自带的加载器
1.启动类加载器(引导类加载器 Bootstrap ClassLoader)
1.1 这个类加载使用C/C++语言实现,嵌套在JVM内部
1.2 它用来加载java的核心库(JAVA_HOME/jre/lib/rt.jar/resource.jar或sum.boot.class.path路径下的内容),用于提供JVM自身需要的类
1.3 并不继承自java.lang.ClassLoader,没有父加载器
1.4 加载扩展类和应用类加载器,并指定为他们的他们的父加载器
1.5 出于安全考虑,BootStrap启动类加载器只加载包为java、javax、sum等开头的类
2.拓展类加载器
2.1 java语言编写,由sun.misc.Launcher$ExtClassLoader实现
2.2 派生于ClassLoader类
2.3 父类加载器为启动类加载器
2.4 从java.ext.dirs系统属性所指定的目录中加载类库或从JDK的安装目录的jre/lib/ext子目录(扩展目录)下加载类库如果用户创建的JAR放在此目录下,也会由拓展类加载器自动加载
3.应用程序类加载器(系统类加载器 App ClassLoader)
3.1java语言编写,由sun.misc.Launcher$AppClassLoader实现。
3.2派生于ClassLoader类
3.3父类加载器为拓展类加载器
3.4它负责加载环境变量classpath或系统属性java.class.path
指定路径下的类库该类加载器是程序中默认的类加载器
一般来说,java应用的类都是由它来完成加载
通过ClassLoader#getSystemClassLoader()方法可以获取到该类加载器
ClassLoader委派图
双亲委派机制
Java虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类才会将她的class文件加载到内存生成的class对象
而且加载某个类的class文件时,java虚拟机采用的是双亲委派模式,既把请求向上委托,它是一种任务委派模式
双亲委派机制的优势
1.避免类的重复加载
2.保护程序安全,防止核心API被随意篡改
沙箱安全机制
自定义String类,但是在加载自定义String类的时候会先使用引导类加载器加载
而引导类加载器在加载过程中会先加载jdk自带的文件(rt.jar包中的java\lang\String.class)
报错信息说没有main方法就是因为加载的是rt.jar包中的String类
这样可以保证对java核心源代码的保护,这就是沙箱安全机制**.**
类比举例:
我们在读写U盘信息时可以用360沙箱
防止U盘内的病毒等对沙箱外的系统构成污染