1.JVM概述
1.1为什么要学习JVM
通过学习JVM ( java Virtual Machine )可以帮助我们理解java程序运行的过程,了解虚拟机中各种机制的实现原理。为后期写出优质的代码做好准备,为向更高的层次提升打好基础。
1.2虚拟机
虚拟机的本质就是在windows中,虚拟一个运行的环境,分为系统虚拟机比如:VMware和程序虚拟机。程序虚拟机的典型代表就是java虚拟机,虚拟机是专门为执行某个单个计算机程序而设计。在java虚拟机中执行的指令我们称为java字节码指令。
1.3JVM作用
(1)负责将字节码加载到内存中(运行时数据区)
(2)负责存储数据
(3)把字节码翻译为机器码并执行
特点:一次编译到处运行——现在的JVM不仅可以执行java字节码,还可以执行其他语言编译后的字节码文件,是一个跨语言平台。自动的内存分配,自动垃圾回收功能。
1.4JVM整体组成
1.类加器(负责加载字节码文件)
2.运行时数据区(存储运时数据,堆,java虚拟机栈(运行java自己的方法),方法区,程序计数器,本地方法栈)
3.执行引擎(更底层,把字节码翻译为机器码)
4.本地方法接口
程序在执行之前要先把java代码转换成字节码(class文件),jvm首先需要把字节码通过类加载器,把文件加载到内存中的运行时数据区。字节码并不能由底层系统直接执行,需要执行引擎将字节码文件翻译成底层系统指令交由cpu去执行,在这个过程中需要调用其他语言的本地方法接口来实现整个程序的功能。
2.JVM结构-类加载
2.1类加载器子系统
2.2类加载过程
使用IO读取字节码文件,转换并存储, 为每个类创建一个Class类的对象,存储在方法区中
链接(验证,准备,解析)
验证: 对字节码文件格式进行验证,文件是否被污染,对基本的语法格式进行验证。
准备: 为静态的变量进行内存分配。
public static int value = 123;value 在准备阶段后的初始值是 0,而不是 123
静态常量(final修饰)在编译期间就初始化。
解析: 将符号引用转为直接引用,将字节码中的表现形式,转为内存中表现(内存地址)。
初始化: 类的初始化,为类中的定义的静态变量进行赋值。
public static int value = 123;value 在初始化阶段后值是 123.
类什么时候会被加载
1.在类中运行main方法
2.创建对象
3.使用类中的静态变量,静态方法
4.反射 Class.forName("类的地址");
5.子类被加载
以下两种情况类不会被初始化:
static final int b = 20; 编译期间赋值的静态常量
System.out.println(User.b);
User[] users = new User[10]; 作为数组类型
2.3类加载器
具体的负责加载类的一些代码
1.引导类加载器:用c/c++语言开发的, jvm底层的开发语言,负责加载java核心类库,与java语言无关的。
2.扩展类加载器:java 语言编写的,由 sun.misc.Launcher$ExtClassLoader 实现,继承ClassLoader类。从 JDK 系统安装目录的 jre/lib/ext 子目录(扩展目录)下加载类库。
3.应用程序类加载器:Java 语言编写的,由 sun.misc.Launcher$AppClassLoader 实现. 派生于 ClassLoader 类。加载程序中自己开发的类。
4.自定义类加载器
双亲委派机制
为了保证程序的安全,防止自己写的类替换核心类, java使用按需加载,当需要加载一个类时,先委托给父类加载器加载,如果父加载器没有找到,继续向上级委托,直到引导类加载器。父级找到就返回,父级如果最终没有找到,抛出ClassNotFoundException,向下委派给子级加载器,找到返回该类,最终没有找到,报ClassNotFoundException。这是因为要先确保加载系统类。
双亲委派机制,是java提供的类加载的规范,但不是强制不能改变的。
我们可以通过自定义的类加载器,改变加载方式.
打破双亲委派机制
可以通过继承ClassLoader类,重写loadClass(实现双亲委派的地方,不推荐)/findClass(推荐)方法,实现自定义的类加载。
典型的tomcat中,加载部署在tomcat中的项目时,就使用的是自己的类加载器