虚拟机:一台虚拟的计算机,它是一款软件,用来执行一系列虚拟机算计指令。可分为系统虚拟机和程序虚拟机。
系统虚拟机:Visual Box,VMware。他们完全是对物理计算机的仿真,提供了一个可完整运行操作系统的软件平台。
程序虚拟机:Java虚拟机(JVM),他专门为执行单个的就是那集程序而设计,在java虚拟机中红执行的指令我们称之为Java字节码指令。
无论是系统虚拟机还是程序虚拟机,在其中运行的软件都被限制于虚拟机提供的资源中。
Java虚拟机(JVM)特点:
1,一次编译处处运行
2,自动内存垃圾回收
3,自动内存管理
类加载过程:
1,通过一个类的全局限定名获取定义此类的二进制字节码流
2,将这个字节码流代表的静态存储结构转换为方法区的运行时数据结构
3,在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
加载字节码文件的方式:
本地系统直接加载
通过网络获取,典型场景:Web Applet
从zip压缩包中读取,成为日后jar,war格式的基础
运行时计算生成,使用最多的是:动态代理技术
有其它的文件生成,典型使用场景:JSP应用
从专用数据中提取.class文件
从加密文件中获取,典型的防治Class文件被反编译的保护措施
验证 (Verify):
目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,保证被加载类的正确行,不会微寒虚拟机自身安全。
准备(Prepare):
为类变量分配内存并且设置该类变量的默认初始值,即零值。这里不包括用final修饰的static,因为final在编译的时候就会分配了,准备阶段会显式初始化。
这里不会为实例变量分配初始化,类变量会分配在方法区中,而实力变量是会随着对象一起分配到Java堆中。
解析(Resolve):
将常量池内的符号引用转换为直接引用的过程。
实际上,解析操作往往伴随着JVM在执行完初始化之后再执行的。
初始化阶段就是啊执行类构造器方法()的过程,该方法不需要定义,是javac编译器自动收集类中的所有类变量的肤质动作和静态代码块中的语句合并而来。构造器方法中指令安装语句在源文件中出现的顺序执行;
类构造器方法()不同于类的构造器,类的构造器是虚拟机视角下的();
如果类具有父类,JVM会保证子类的()执行前,父类的()已经执行完毕。
虚拟机必须保证一个类的()方法在多线程下被同步加锁。
这个类加载器使用C/C++ 实现,嵌套在JVM内部;
它用来加载java的核心库JAVA_HOME/jre/lib/rt.jar、resources.jar或者sun.boot.class.path路径下的内容),用于提供JVM自身需要的类;
并不继承自java.lang.ClassLoader,他没有父加载器;
该加载器会加载扩展类加载器和程序类加载器,并成为他们的负累加载器。
出于安全考虑,BootStrapClassLoader值加载包名为java,javax,sun等开头的类。
Java语言编写,由sun.misc.Launcher$ExtClassLoader实现;
派生于ClassLoader类;
父类加载器为启动类加载器;
从java.ext.dirs系统属性指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录(扩展目录)下加载类库。如果用户创建的JAR放在此目录下,也会自动有扩展类加载器加载。
Java语言编写,由sun.misc.Launcher$AppClassLoader实现
派生于ClassLoader类
父类加载器为扩展类加载器
他负责加载环境变量classpath或系统属性java.class.path 指定路径下的类库;
该类加载器是程序中默认的类加载器,一般情况下,Java应用的类都是由它类完成加载
通过ClassLoader.getSysttemClassLoader()方法可以获取到该类加载器。
作用:隔离加载类,修改类加载的方式,扩展加载源,防止源码泄漏。
类加载实例代码:
public static void main(String[] args){
//系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println("系统类加载器:"+systemClassLoader);
//获取其上层:扩展类加载器
ClassLoader extClassLoader = systemClassLoader.getParent();
System.out.println("扩展类加载器:"+extClassLoader);
//获取其上层:引导类加载器(获取不到)
ClassLoader boostrapClassLoader = extClassLoader.getParent();
System.out.println("引导类加载器:"+boostrapClassLoader);
//对于用户自定义类来说默认使用的系统类加载器
ClassLoader userClassLoader = ClassLoaderTest.class.getClassLoader();
System.out.println("应用程序使用的类加载器:"+userClassLoader);
//java核心类使用的引导类加载器
ClassLoader stringClassLoader = String.class.getClassLoader();
System.out.println("java核心类使用的类加载器:"+stringClassLoader);
}
类加载器的获取方式:
通过Class类对象获取;
从线程的上下文环境中获取;
类加载器获取父类加载器。
System.out.println(ClassLoaderTest.class.getClassLoader());
System.out.println(Thread.currentThread().getContextClassLoader());
System.out.println(Thread.currentThread().getContextClassLoader().getParent());
Java虚拟机对class文件采用的是按需加载的方式,也就是说当前需要使用该类是曹慧将它的class文件加载到内存生成class队形。而且加载某个类的class文件时,Java虚拟机采用的是双清委派模式,即把请求交由父类处理,它是一种任务委派模式。
1,如何一个类加载器收到了类加载请求,他并不会自己先去加载,而是把这个请求委托给父类的加载器去执行;
2,如果父类加载器还存在器父类加载器,则进一步向上委托,一次递归,请求最终到达顶层的启动类加载器;
3,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成任务,自加载器才会尝试自己去加载。
避免类的重复加载
保护程序安全,防止核心API被随意篡改
package java.lang;
public class String {
public static void main(String[] args) {
System.out.println("自定义的java.lang.String类");
}
}
Java开头的类由BootStrapClassLoader负责加载,虚拟机启动时就已经加载了,不会再加载用户自定义的java.lang.String类,所以找不到main方法。
在jvm中表示两个class对象是否为同一个类存在两份必要条件:
1,类的完全类名必须一致,包括包名。
2,加载这个类的ClassLoader(ClassLoader的实例对象)必须相同。
也就是说在JVM中,即使两个类对象(class对象)来源于同一个Class文件,被同一个虚拟机锁加载,但只要加载他们呢的ClassLoader实例对象不一样,那么着两个类对象也是不相等的。