JVM学习之类加载
该文使用Hotspot JDK1.7
一、类加载器
1、什么是类加载器
类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。完成类加载的家伙就是类加载器。
2、都有哪些类加载器
package com.jalja.org.base.classLoader; public class Test01 {
public static void main(String[] args) {
ClassLoader loader=Test01.class.getClassLoader();
while(loader!=null){
System.out.println(loader.getClass().getName());
loader=loader.getParent();//获取父类加载器
}
System.out.println(loader);
}
} 结果:
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
null
由输出结果可以看出ExtClassLoader是AppClassLoader的父类,而ExtClassLoader的父类却是null,原因是ExtClassLoader的父类加载器是BootStrap,BootStrap是JVM最底层的引导类加载器用C语言编写的,所以找不到一个确定的返回父Loader的方式,于是就返回null。
为什么要有这个引导类加载器:
类加载器也是java类,他们也需要类加载器加载进入内存,显然必须要有第一个不是java类的类加载器,来完成这个工作,这个正是BootStrap。
JVM中类加载器的结构:
3、各个类加载器的作用
BootStrap ClassLoader(启动类加载器):负责加载存放在D:\Program Files (x86)\Java\jdk1.7.0_79\jre\lib下,或被-Xbootclasspath参数指定的路径中的,并且能被虚拟机识别的类库(如rt.jar,所有的java.*开头的类均被Bootstrap ClassLoader加载)。启动类加载器是无法被Java程序直接引用的。
Extension ClassLoader(扩展类加载器):该加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载D:\Program Files (x86)\Java\jdk1.7.0_79\jre\lib\ext目录中,或者由java.ext.dirs系统变量指定的路径中的所有类库(如javax.*开头的类),开发者可以直接使用扩展类加载器。
Application ClassLoader(应用程序类加载器):该类加载器由sun.misc.Launcher$AppClassLoader来实现,它负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
4、JVM类加载机制(JVM需要加载一个类时,到底会派出哪个类加载器去执行?)
•全盘负责,当前线程的类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用CLassLoader.loadClass()指定类加载器来载入
•父类委托,先让父类加载器试图加载该类,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。所以我们在开发中尽量不要使用与JDK相同的类(例如自定义一个java.lang.System类),因为父类加载器中已经有一份java.lang.System类了,它会直接将该类给程序使用,而你自定义的类压根就不会被加载。
双亲委派模型:
双亲委派模型的工作流程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,
只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。
双亲委派机制:
1、当AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。
2、当ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrap ClassLoader去完成。
3、如果BootStrap ClassLoader加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),会使用ExtClassLoader来尝试加载;
4、若ExtClassLoader也加载失败,则会使用AppClassLoader来加载,如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException。
双亲委派模型意义:
-系统类防止内存中出现多份同样的字节码
-保证Java程序安全稳定运行
•缓存机制,缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区寻找该Class,只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区。这就是为什么修改了Class后,必须重启JVM,程序的修改才会生效。
5、自定义类加载器
二、类的加载
1、类的加载:
类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。
字节码(.class)文件来源:
– 从本地系统中直接加载
– 通过网络下载.class文件
– 从zip,jar等归档文件中加载.class文件
– 从专有数据库中提取.class文件
– 将Java源文件动态编译为.class文件
2、类加载的过程
JVM将javac编译好的class文件加载到内存中,并对该数据进行验证、解析和初始化,最终形成JVM可以直接使用的JAVA类型的过程。
(1)、加载:加载阶段其实就是JVM通过一个类的全限定名来获取其定义的二进制字节流,并将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构且在Java堆中生成一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口。在该阶段我们开发人员可以干预,例如:我们可以指定类加载器来加载该字节数组或者自定义类加载器来加载。
(2)、链接:将java类的二进制代码合并到JVM的运行状态中的过程
a、验证:验证是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
b、准备:该阶段是在方法区中为类变量(static变量)分配内存并设置类变量初始值。例如:public static int flag=1;该阶段初始化值为0。
c、解析:虚拟机将常量池中的符号引用替换为直接引用的过程。(直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄)
(3)、初始化:初始化为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化。
- 初始化阶段就是执行类构造器<clinit>()的过程,类构造器<clinit>()是由编译器自动收集类中的所有类变量的赋值动作和静态语句块中的语句合并产生的。
- 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先初始化其父类。
- 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。
- 当访问一个java 类的静态域时,只有正真申明这个域的类才会被初始化。
类的初始化时机:
- 当虚拟机启动则一定会加载main方法所在的类
package com.jalja.org.base.classLoader;
public class A {
public static int width=100;
static{
System.out.println("静态代码块");
}
public static void main(String[] args) { }
}
结果:
静态代码块
- 当初始化一个类时,如果其父类未被初始化则一定先初始化其父类
package com.jalja.org.base.classLoader;
public class Demo {
public static void main(String[] args) {
A a=new A();
}
}
class A extends FatherA{
static{
System.out.println("静态代码块A");
}
}
class FatherA{
static{
System.out.println("静态代码块FatherA");
}
} 结果:
静态代码块FatherA
静态代码块A
- new一个对象的时候类被加载
- 调用类的静态方法和静态成员变量(除了final常量 常量在编译阶段就存入调用类的常量池中,所以无需初始化类)
- 使用java.lang.reflect包中的方法进行反射调用类会被初始化
类的被动引用不会初始化类:
- 调用final常量不会初始化应为 常量在编译阶段就存入调用类的常量池中,所以无需初始化类。
- 通过数组定义的类引用不会初始化类
package com.jalja.org.base.classLoader;
public class Demo {
public static void main(String[] args) {
A[] as=new A[10];
}
}
class A{
static{
System.out.println("静态代码块A");
}
}
无结果输出
- 当访问一个静态变量时,只有正真声明这个静态变量的类才会被初始化(通过子类调用父类的静态变量,子类不会被初始化)
package com.jalja.org.base.classLoader;
public class Demo {
public static void main(String[] args) {
System.out.println(A.width);
}
}
class A extends FatherA{
static{
System.out.println("静态代码块A");
}
}
class FatherA{
public static int width=100;
static{
System.out.println("静态代码块FatherA");
}
} 结果:
静态代码块FatherA
100
(4)、卸载
Java虚拟机将结束生命周期的时机:
- 执行了System.exit()方法
- 程序正常执行结束
- 程序在执行过程中遇到了异常或错误而异常终止
- 由于操作系统出现错误而导致Java虚拟机进程终止
JVM学习之类加载的更多相关文章
- Java虚拟机JVM学习05 类加载器的父委托机制
Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...
- JVM学习笔记——类加载过程
JVM学习笔记——类加载过程 类加载模型——双亲委派模型(Parents Delegation Model)也可称为“溯源委派加载模型” Java的类加载器是一个运行时核心基础设施模块,主要是启动之初 ...
- JVM学习笔记——类加载和字节码技术篇
JVM学习笔记--类加载和字节码技术篇 在本系列内容中我们会对JVM做一个系统的学习,本片将会介绍JVM的类加载和字节码技术部分 我们会分为以下几部分进行介绍: 类文件结构 字节码指令 编译期处理 类 ...
- JVM学习--(六)类加载器原理
我们知道我们编写的java代码,会经过编译器编译成字节码文件(class文件),再把字节码文件装载到JVM中,映射到各个内存区域中,我们的程序就可以在内存中运行了.那么字节码文件是怎样装载到JVM中的 ...
- JVM学习记录-类加载时机
虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是类的加载机制. 在Java语言里面,类型的加载.连接和初始化过程都 ...
- JVM学习记录-类加载的过程
类的整个生命周期的7个阶段是:加载(Loading).验证(Verification).准备(Preparation).解析(Resolution).初始化(Initialization).使用(Us ...
- JVM学习记录-类加载器
前言 JVM设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外面去实现,以便让应用程序自己决定如何去获取所需要的类.实现这个动作的代码模块称为“类 ...
- [jvm学习笔记]-类加载过程
JVM类加载的过程 加载=>验证=>准备=>解析=>初始化 5个阶段所执行的具体动作 加载 在加载阶段,虚拟机需要完成3个事情1.通过一个类的全限定名获取定义此类的二进制字节流 ...
- JVM学习笔记——类加载器与类加载过程
类加载器与类加载过程 类加载器ClassLoader 类加载器 ClassLoader 用于把 class 文件装载进内存. 启动类加载器(Bootstrap ClassLoader): 这个类加载使 ...
随机推荐
- js实时显示系统时间
刚刚在做后台页面最上面要动态显示时间刚写了这个代码 将这段代码加入<head></head> <!--时间显示代码 --><script>functio ...
- windows下安装php5.2.*,php5.3.*,php5.4.*版本的memcache扩展
注:如使用集成环境成功率低,请自行配置php apache,表示win7下wamp php5.4.3基础上配置拓展,成功率极低.费时. 拓展安装调试方法: 编写调试php文件 <?php me ...
- 基于Daydream technical preview GVR13开发Daydream,Cardboard的Android应用
本文用Unity的Daydream Preview GVR13版本开发同时兼容Daydream和Cardboard的Android应用,Android Studio版本为2.2.3. 下载最新Dayd ...
- 【原】小写了一个cnode的小程序
小程序刚出来的第一天,朋友圈被刷屏了,所以趁周末也小玩了一下小程序.其实发觉搭建一个小程序不难,只要给你一个demo,然后自己不断的查看文档,基本就可以入门了,不过对于这种刚出来的东西,还是挺多坑的, ...
- js中的正则表达式入门
什么是正则表达式呢? 正则表达式(regular expression)描述了一种字符串匹配的模式,可以用来检查一个字符串是否含有某种子串.将匹配的子串做替换或者从某个字符串中取出符合某个条件的子串等 ...
- 如何编写一个gulp插件
很久以前,我们在"细说gulp"随笔中,以压缩JavaScript为例,详细地讲解了如何利用gulp来完成前端自动化. 再来短暂回顾下,当时除了借助gulp之外,我们还利用了第三方 ...
- We Chall-Training: Stegano I-Writeup
MarkdownPad Document html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,ab ...
- C语言 extern学习1
没有头文件时,通过本文件内的函数声明来确定定义域,实现功能: //单文件测试 #include <stdio.h> /* 经测试,C语言环境下子函数默认是void型:所以可省略不写 为严谨 ...
- 移动端touch实现下拉刷新
移动端实现下拉刷新 第一部分:四个touch事件 1.touchstart:只要将手指放在了屏幕上(而不管是几只),都会触发touchstart事件. 2.touchmove: 当我们用手指在屏幕上滑 ...
- 在GlassFish应用服务器上创建并运行你的第一个Restful Web Service【翻译】
前言 本人一直开发Android应用,目前Android就业形势恶劣,甚至会一路下滑,因此决定学习服务器开发.采用的语言是java,IDE是Intellij,在下载Intellij的同时看到官网很多优 ...