「java类加载机制是什么」java类的加载机制及加载过程
今天给各位分享java类加载机制是什么的知识,其中也会对java类的加载机制及加载过程进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!
本文目录一览:
描述一下JVM加载class文件的原理?
Java语言是一种具有动态性的解释型语言,类(class)只有被加载到JVM中后才能运行。当运行指定程序时,JVM会将编译生成的.class文件按照需求和一定的规则加载到内存中,并组织成为一个完整的Java应用程序。这个加载过程是由类加载器来完成的,具体来说,就是由ClassLoader和它的子类来实现的。类加载器本身也是一个类,其实质是把类文件从硬盘读取到内存中。
类的加载方式分为隐式加载与显式加载两种。隐式加载指的是程序在使用new等方法创建对象时,会隐式地调用类的加载器把对应的类加载到JVM中。显式加载指的是通过直接调用class.forName()方法来把所需要的类加载到JVM中。
任何一个工程项目都是由许多个类组成的,当程序启动时,只把需要加载的类加载到JVM中,其他类只有被使用到的时候才会被加载,采用这种方法,一方面可以加快加载速度,另外一方面可以节约程序运行过程中对内存的开销。此外,在Java语言中,每个类或接口都对应一个.class文件,这些文件可以被看成一个个可以被动态加载的单元,因此当只有部分类被修改时,只需要重新编译变化的类即可,而不需要重新编译所有文件,因此加快了编译速度。
VM 加载class文件的原理是什么?
1. Java中的所有类,必须被装载到JVM中才能运行,这个装载工作是由JVM中的类装载器完成的,类装载器所做的工作实质是把类文件从硬盘读取到内存中,作用就是在运行时加载类。
Java类加载器基于三个机制:委托、可见性和单一性。
(1)委托机制是指加载一个类的请求交给父类加载器,如果这个父类加载器不能够找到或加载这个类,那么再加载它。
(2)可见性的原理是子类的加载器可以看见所有的父类加载器加载的类,而父类加载器看不到子类加载器加载的类。
(3)单一性原理是指一个类仅被加载一次,这是由委托机制确保子类加载器不会再次加载父类加载器加载过的类。
2. Java中的类大致分为三种:
(1)系统类
(2)扩展类
(3)由程序员自定义的类
3. 类装载有两种方式
(1)隐式装载:
程序在运行过程中当碰到通过new等方式生成类或者子类对象、使用类或者子类的静态域时,隐式调用类加载器加载对应的的类到JVM中。
(2)显式装载:
通过调用Class.forName()或者ClassLoader.loadClass(className)等方法,显式加载需要的类。
4. 类加载的动态性体现
一个应用程序总是由n多个类组成,Java程序启动时,并不是一次把所有的类全部加载再运行,他总是把保证程序运行的基础类一次性加载到JVM中,其他类等到JVM用到的时候再加载,这样是为了节省内存的开销,因为Java最早就是为嵌入式系统而设计的,内存宝贵,而用到时再加载这也是Java动态性的一种体现。
5. Java类加载器
Java中的类加载器实质上也是也是类,功能是把类加载入JVM中,值得注意的是JVM的类加载器有三个,原因有:一方面是为了分工明确,各自负责各自的区块,另一方面为了实现委托模型。
层次结构如下:
BootStrap Loader(引导类加载器) ----- 负责加载系统类
ExtClassLoader(扩展类加载器) ----- 负责加载扩展类
AppClassLoade(应用类加载器)r ----- 负责加载应用类
6. 类加载器之间如何协调工作的
Java中有三个类加载器,碰到一个类需要加载时,Java采用委托模型机制来协调和区分该由哪个类加载器完成。简单来说就是,“类装载器有载入类的需求时,会先请示其Parent使用其搜索路径帮忙载入”,如果Parent找不到,那么才由自己依照自己的搜索路径搜索类。
类加载器(初始化)
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。如果不出现意外情况,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或者类初始化
类的加载
就是指将class文件读入内存,并为之创建一个 java.lang.Class 对象
任何类被使用时,系统都会为之建立一个 java.lang.Class 对象
类的连接
验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致
准备阶段:负责为类的类变量分配内存,并设置默认初始化值
解析阶段:将类的二进制数据中的符号引用替换为直接引用
类的初始化
在该阶段,主要就是对类变量进行初始化
假如类还未被加载和连接,则程序先加载并连接该类
假如该类的直接父类还未被初始化,则先初始化其直接父类
假如类中有初始化语句,则系统依次执行这些初始化语句
注意:在执行第2个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3
创建类的实例
调用类的类方法
访问类或者接口的类变量,或者为该类变量赋值
使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
初始化某个类的子类
直接使用java.exe命令来运行某个主类
负责将.class文件加载到内存中,并为之生成对应的 java.lang.Class 对象
虽然我们不用过分关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行
全盘负责:就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
缓存机制:保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区中搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存储到缓存区
ClassLoader:是负责加载类的对象
Java运行时具有以下内置类加载器
1)Bootstrap class loader:它是虚拟机的内置类加载器,通常表示为null ,并且没有父null
2)Platform class loader:平台类加载器可以看到所有平台类 ,平台类包括由平台类加载器或其祖先定义的Java SE平台API,其实现类和JDK特定的运行时类
3) System class loader:它也被称为应用程序类加载器 ,与平台类加载器不同。 系统类加载器通常用于定义应用程序类路径,模块路径和JDK特定工具上的类
ClassLoader 中的两个方法
1) static ClassLoader getSystemClassLoader():返回用于委派的系统类加载器
2) ClassLoader getParent():返回父类加载器进行委派
java 类加载机制有什么用
AVA类加载机制详解
“代码编译的结果从本地机器码转变为字节码,是存储格式发展的一小步,却是变成语言发展的一大步”,这句话出自《深入理解JAVA虚拟机》一书,后面关于jvm的系列文章主要都是参考这本书。
JAVA源码编译由三个过程组成:
1、源码编译机制。
2、类加载机制
3、类执行机制
我们这里主要介绍编译和类加载这两种机制。
一、源码编译
代码编译由JAVA源码编译器来完成。主要是将源码编译成字节码文件(class文件)。字节码文件格式主要分为两部分:常量池和方法字节码。
二、类加载
类的生命周期是从被加载到虚拟机内存中开始,到卸载出内存结束。过程共有七个阶段,其中到初始化之前的都是属于类加载的部分
加载----验证----准备----解析-----初始化----使用-----卸载
系统可能在第一次使用某个类时加载该类,也可能采用预加载机制来加载某个类,当运行某个java程序时,会启动一个java虚拟机进程,两次运行的java程序处于两个不同的JVM进程中,两个jvm之间并不会共享数据。
1、加载阶段
这个流程中的加载是类加载机制中的一个阶段,这两个概念不要混淆,这个阶段需要完成的事情有:
1)通过一个类的全限定名来获取定义此类的二进制字节流。
2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3)在java堆中生成一个代表这个类的Class对象,作为访问方法区中这些数据的入口。
由于第一点没有指明从哪里获取以及怎样获取类的二进制字节流,所以这一块区域留给我开发者很大的发挥空间。这个我在后面的类加载器中在进行介绍。
2、准备阶段
这个阶段正式为类变量(被static修饰的变量)分配内存并设置类变量初始值,这个内存分配是发生在方法区中。
1、注意这里并没有对实例变量进行内存分配,实例变量将会在对象实例化时随着对象一起分配在JAVA堆中。
2、这里设置的初始值,通常是指数据类型的零值。
private static int a = 3;
这个类变量a在准备阶段后的值是0,将3赋值给变量a是发生在初始化阶段。
3、初始化阶段
初始化是类加载机制的最后一步,这个时候才正真开始执行类中定义的JAVA程序代码。在前面准备阶段,类变量已经赋过一次系统要求的初始值,在初始化阶段最重要的事情就是对类变量进行初始化,关注的重点是父子类之间各类资源初始化的顺序。
java类中对类变量指定初始值有两种方式:1、声明类变量时指定初始值;2、使用静态初始化块为类变量指定初始值。
初始化的时机
1)创建类实例的时候,分别有:1、使用new关键字创建实例;2、通过反射创建实例;3、通过反序列化方式创建实例。
new Test();
Class.forName(“com.mengdd.Test”);
2)调用某个类的类方法(静态方法)
Test.doSomething();
3)访问某个类或接口的类变量,或为该类变量赋值。
int b=Test.a;
Test.a=b;
4)初始化某个类的子类。当初始化子类的时候,该子类的所有父类都会被初始化。
5)直接使用java.exe命令来运行某个主类。
除了上面几种方式会自动初始化一个类,其他访问类的方式都称不会触发类的初始化,称为被动引用。
1、子类引用父类的静态变量,不会导致子类初始化。
public class SupClass
{ public static int a = 123;
static
{
System.out.println("supclass init");
}
}public class SubClass extends SupClass
{ static
{
System.out.println("subclass init");
}
}public class Test
{ public static void main(String[] args)
{
System.out.println(SubClass.a);
}
}
执行结果:
supclass init123
2、通过数组定义引用类,不会触发此类的初始化
public class SupClass
{ public static int a = 123;
static
{
System.out.println("supclass init");
}
}public class Test
{ public static void main(String[] args)
{
SupClass[] spc = new SupClass[10];
}
}
执行结果:
3、引用常量时,不会触发该类的初始化
public class ConstClass
{ public static final String A= "MIGU";
static
{
System.out.println("ConstCLass init");
}
}public class TestMain
{ public static void main(String[] args)
{
System.out.println(ConstClass.A);
}
}
执行结果:
MIGU
用final修饰某个类变量时,它的值在编译时就已经确定好放入常量池了,所以在访问该类变量时,等于直接从常量池中获取,并没有初始化该类。
初始化的步骤
1、如果该类还没有加载和连接,则程序先加载该类并连接。
2、如果该类的直接父类没有加载,则先初始化其直接父类。
3、如果类中有初始化语句,则系统依次执行这些初始化语句。
在第二个步骤中,如果直接父类又有直接父类,则系统会再次重复这三个步骤来初始化这个父类,依次类推,JVM最先初始化的总是java.lang.Object类。当程序主动使用任何一个类时,系统会保证该类以及所有的父类都会被初始化。
关于java类加载机制是什么和java类的加载机制及加载过程的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。
发布于:2022-12-06,除非注明,否则均为
原创文章,转载请注明出处。