深入理解JVM-类加载器深入解析(2)
深入理解JVM-类加载器深入解析(2)
加载:就是把二进制形式的java类型读入java虚拟机中
连接:
验证:
准备:为类变量分配内存,设置默认值.但是在到达初始化之前,类变量都没有初始化为真正的初始值
解析:解析过程就是在类型的变量的常量池中寻找类,接口,字段和方法的符号引用,把这些符号引用替换成直接引用的过程
初始化:为类变量赋予正确地初始值
类实例化:
为新的对象分配内存
为实例变量赋默认值
为实例变量赋正确地初始值
java编译器为它编译的每一个类都至少生成一个实例初始化方法,在java的class文件中,这个实例初始化方法被称为"".针对源代码中每一个类的构造方法,java编译器都产生一个方法
类的加载
类的加载的最终产品是位于内存中的Class对象
class对象封装了类在方法区内的数据结构,并且向java程序员提供了访问方法区内的数据结构的接口
有两种类型的类加载器
- java虚拟机自带的加载器
根类加载器(Bootstrap)
扩展类加载器(Extension)
系统(应用)类加载器(System)
- 用户自定义的类加载器
java.lang.ClassLoader的子类
用户可以定制类的加载方式
类加载器并不需要等到某个类被"首次主动使用"时再加载它
JVM规范允许类加载器在预料某个类将要被使用时就预先加载它,如果在预先加载的过程中遇到了.class文件缺失或存在错误,类加载器必须在程序首次主动使用该类时才报告错误(LinkageError错误)
如果这个类一直没有被程序主动使用,那么类加载器就不会报告错误
类的验证
类被加载后,就进入了连接阶段.连接就是将已经读入到内存的类的二进制数据合并到虚拟机的运行时环境中去.
类的验证的内存
类文件的结构检查
语义检查
字节码验证
二进制兼容性的验证
类的准备
在准备阶段,Java虚拟机为类的静态变量分配内存,并设置默认的初始值.例如对于以下Sample类,在准备阶段,将为int类型的静态变量a分配4个字节的内存空间,并且赋予默认值0,为long类型的静态变量b分配8个字节的内存空间,并且赋予默认值0.
public class Sample{
private static int a = 1;
public static long b;
static {
b=2;
}
}
类的初始化
在初始化阶段,Java虚拟机执行类的初始化语句,为类的静态变量赋予初始值.在程序中,静态变量的初始化有两种途径;
静态变量的生命处进行初始化;
在静态代码块中进行初始化.
例如在以下代码中,静态变量a和b都被显示初始化,而静态变量c没有被显示初始化,它将保持默认值0
public class Sample{
private static int a=1; //在静态变量的声明处进行初始化
public static long b;
public static long c;
static{
b=2; // 在静态代码块中进行初始化
}
}
静态变量的声明语句,以及静态代码块都被看做类的初始化语句,java虚拟机会按照初始化语句在类文件中的先后顺序来一次执行它们.例如当一下Sample类被初始化后,它的静态变量a的取值为4.
public class Sample{
static int a= 1;
static {a=2;}
static {a=4;}
public static void main(String args[]){
System.out.println("a="+a)//打印a=4
}
}
类的初始化步骤:
加入这个类还没有被加载和连接,那就先进行加载和连接
加入类存在直接父类,并且这个父类还没有被初始化,那就先初始化直接父类
加入类中存在初始化语句,那就一次执行这些初始化语句
当java虚拟机初始化一个类时,要求它的所有父类都已经被初始化,但是这条规则并不适用于接口.
在初始化一个类时,并不会先初始化它所实现的接口.
在初始化一个接口时,并不会先初始化它的父接口
/**
* 当一个接口在初始化时,并不要求其父接口都完成了初始化
* 只有在真正使用到父接口的时候(如引用接口中所定义的常量时),才会初始化
*
* 在加上-XX:TraceClassLoading这个参数以后我们可以得知虚拟机的加载情况
* 如果是MyChild5是Class类,并且b变量是public static的时候可以看得到
* MyParent5和MyChild5都被加载了;如果b变量改为了public static final类型的
* 则可以看到MyParent5和MyChild5都没有被加载
*
* 如果MyChild5是接口的话,则b变量默认是public static final类型的产量,是在
* 所以在编译阶段就会存入调用这个常量所在的方法的类的常量池中,所以并不会去加载MyParent5和MyChild5字节码文件
*
* 初始化:
* 我们在MyParent5中加入一个静态变量,如果MyParent5接口被初始化,一定会打印出MyParent5 invoked
* 但是实际上并不会打印出,如果把MyParent5 改成了class类的话,则会打印出,
* 说明在初始化一个类时,并不会先初始化它所实现的接口
*
* 我们再定义MyParent5_1和MyGrandpa5_1,在直接调用MyParent5_1的时候我们可以知道MyParent5_1被初始化了,
* 但是并没有打印出MyGrandpa5_1 invoked,说明接口的父类并不会被初始化
*/
public class MyTest5 {
public static void main(String[] args) {
//System.out.println(MyChild5.b);
System.out.println(MyParent5_1.THREAD);
}
}
interface MyGrandpa5{
public static Thread THREAD = new Thread(){
{
System.out.println("MyGrandpa5 invoked");
}
};
}
interface MyParent5 {
//class MyParent5 {
public static int a = 5;
//public static int a = new Random().nextInt(5);
public static Thread THREAD = new Thread(){
{
System.out.println("MyParent5 invoked");
}
};
}
class MyChild5 implements MyParent5 {
public static int b = 6;
//public static int b = new Random().nextInt(4);
}
interface MyGrandpa5_1{
public static Thread THREAD = new Thread(){
{
System.out.println("MyGrandpa5_1 invoked");
}
};
}
interface MyParent5_1 {
public static int a = 5;
//public static int a = new Random().nextInt(5);
public static Thread THREAD = new Thread(){
{
System.out.println("MyParent5_1 invoked");
}
};
public static Thread THREAD2 = new Thread(){
{
System.out.println("MyParent5_1 invoked2");
}
};
}
因此,一个父接口并不会因为它的子接口或者实现类的初始化而初始化.只有当程序首次适用特定接口的静态变量时,才会导致该接口的初始化.
只有当程序访问的静态变量或静态方法确实在当前类或当前接口中定义时,才可以认为是对类或接口的主动使用
调用ClaossLoader类的loadClass方法加载一个类,并不是对类的主动使用,不会导致类的初始化
类加载器
类加载器用来把类加载到java虚拟机中.从JDK1.2版本开始,类的加载过程才用父类委托机制,这种机制能更好地保证java平台的安全.在此委托机制中,除了java虚拟机自带的根类加载器以外,其余的类加载器都有且只有一个父加载器.当java程序请求加载器loader1加载Sample类时,loader1首先委托自己的父加载器去加载Sample类,若父加载器能加载,则由父加载器完成加载任务,否则才由加载器loader1本身加载Sample类
在父亲委托机制中,各个加载器按照父子关系形成了树形结构,除了根类加载器之外,其余的类加载器都有且只有一个父加载器
java虚拟机自带了以下几种加载器:
根类加载器(Bootstrap):该加载器没有付加载器.它负责加载虚拟机的核心类库,如java.lang.*等.根类加载器从系统属性sun.boot.class.path所指定的目录中加载类库.根类加再起的实现依赖于底层操作系统,属于虚拟机的实现的一部分,它并没有继承java.lang.ClassLoader类
扩展类加载器(Extension):它的父加载器为根类加载器.它从java.ext.dirs系统属性所指定的目录中加载类库,或者从JDK的安装目录的jre\lib\ext子目录(扩展目录)下加载类库,如果把用户创建的JAR文件放在这个目录下,也会自动由扩展类加载器加载.扩展类加载器是纯java类,是java.lang.ClassLoader类的子类
系统类加载器(System):也称为应用类加载器,它的附加在其为扩展类加载器.它从环境变量classpath或者系统属性java.class.path所指定的目录中加载类,它是用户自定义的类加载器的默认父加载器.系统类加载器是纯java类,是java.lang.ClassLoader类的子类
除了以上虚拟机自带的加载器外,用户还可以定制自己的类加载器.java提供了抽象类java.lang.ClassLoader,所有用户自定义的类加载器都应该继承ClassLoader类
从表象上来看这些类加载器是一种继承关系,实际上应该是一种包含关系
class Parent2 {
static int a =3;
static {
System.out.println("parent2 static block");
}
}
class Child2 extends Parent2{
static int b = 4;
static {
System.out.println("Child2 static block");
}
}
public class MyTest10 {
static {
System.out.println("Mytest10 static block");
}
public static void main(String[] args) {
/**
* Mytest10 static block
* ----
* parent2 static block
* ----
* 3
* ----
* Child2 static block
* 4
*/
Parent2 parent2;
System.out.println("----");
parent2 = new Parent2();
System.out.println("----");
System.out.println(parent2.a);
System.out.println("----");
System.out.println(Child2.b);
}
}
class Parent3 {
static int a = 3;
static {
System.out.println("Parent3 static block");
}
static void doSomething() {
System.out.println("do something");
}
}
class Child3 extends Parent3 {
static {
System.out.println("Child3 static block");
}
}
public class MyTest11 {
public static void main(String[] args) {
/**
* Parent3 static block
* 3
* ----
* do something
* 调用Child3.a定义在父类中,所以是对父类的一个主动使用,
* 谁拥有这个静态变量就是对谁的一个主动使用
* 如果是用子类去调用父类的静态变量也好,静态方法也好,都是对父类的主动使用
*
*/
System.out.println(Child3.a);
System.out.println("----");
Child3.doSomething();
}
}
class CL{
static {
System.out.println("Class CL");
}
}
//调用ClassLoader类的loadClass方法加载一个类,并不是对类的主动使用,不会导致类的初始化
public class MyTest12 {
public static void main(String[] args) throws Exception {
/**
* class jvm.classloader.CL
* ----
* Class CL
* class jvm.classloader.CL
*
*/
ClassLoader loader = ClassLoader.getSystemClassLoader();
Class<?> aClass = loader.loadClass("jvm.classloader.CL");
System.out.println(aClass);
System.out.println("----");
aClass = Class.forName("jvm.classloader.CL");
System.out.println(aClass);
}
}
java学习笔记# #jvm#
深入理解JVM-类加载器深入解析(2)的更多相关文章
- JVM 类加载器深入解析以及重要特性剖析
1.类加载流程图 从磁盘加载到销毁的完整过程. 2.类加载流程图2 1.加载: 就是把二进制形式的java类型读入java虚拟机中 2.连接: 验证.准备.解析. 连接就是将已经读入到内存的类的二进制 ...
- 深入理解Java类加载器(一):Java类加载原理解析
摘要: 每个开发人员对java.lang.ClassNotFoundExcetpion这个异常肯定都不陌生,这个异常背后涉及到的是Java技术体系中的类加载机制.本文简述了JVM三种预定义类加载器,即 ...
- 深入理解Java类加载器(ClassLoader)
深入理解Java类加载器(ClassLoader) Java学习记录--委派模型与类加载器 关于Java类加载双亲委派机制的思考(附一道面试题) 真正理解线程上下文类加载器(多案例分析) [jvm解析 ...
- 深入理解Java类加载器(ClassLoader) (转)
转自: http://blog.csdn.net/javazejian/article/details/73413292 关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Ja ...
- 深入理解JVM虚拟机6:深入理解JVM类加载机制
深入理解JVM类加载机制 简述:虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 下面我们具体 ...
- 深入理解Java类加载器(二):线程上下文类加载器
摘要: 博文<深入理解Java类加载器(一):Java类加载原理解析>提到的类加载器的双亲委派模型并不是一个强制性的约束模型,而是Java设计者推荐给开发者的类加载器的实现方式.在Java ...
- JVM类加载器的分类
类加载器的分类 JVM支持两种类型的类加载器,分别为引导类加载器(Bootstrap ClassLoader)和自定义类加载器(User-Defined ClassLoader). 从概念上来讲,自定 ...
- 深入JVM类加载器机制,值得你收藏
先来一道题,试试水平 public static void main(String[] args) { ClassLoader c1 = ClassloaderStudy.class.getClass ...
- 深入理解Java类加载器(1):Java类加载原理解析
1 基本信息 每个开发人员对Java.lang.ClassNotFoundExcetpion这个异常肯定都不陌生,这背后就涉及到了java技术体系中的类加载.Java的类加载机制是技术体系中比较核心的 ...
- JVM 类加载器命名空间深度解析与实例分析
一.创建Sample 1.创建实例 public class MyPerson { private MyPerson myPerson; public void setMyPerson(Object ...
随机推荐
- chrome如何查看cookie
以mac为例: 第一步:点击chrome的偏好设置 第二步:点击如下图所示的最下面的高级 第三步:点击内容设置,如下所示 第四步:点击cookie,就会出现查看所有cookie和网站数据
- 微信小程序地图开发总结
最近在做一个微信小程序地图插件,通过传入起始位置名称和经纬度信息,就可以跳转到路线规划插件页面中,在该页面中,可以根据起始位置查询自驾,公共交通,步行等方式的路线信息,并且在地图上显示路线信息,在这个 ...
- selenium3+python3自动化测试学习之网页元素定位
selenium基础实战之定位网页元素技巧 selenium定位网页元素 find_element_by_id,find_element_by_name,find_element_by_class_n ...
- HDU 4057:Rescue the Rabbit(AC自动机+状压DP)***
http://acm.hdu.edu.cn/showproblem.php?pid=4057 题意:给出n个子串,串只包含‘A’,'C','G','T'四种字符,你现在需要构造出一个长度为l的串,如果 ...
- POJ 3321:Apple Tree + HDU 3887:Counting Offspring(DFS序+树状数组)
http://poj.org/problem?id=3321 http://acm.hdu.edu.cn/showproblem.php?pid=3887 POJ 3321: 题意:给出一棵根节点为1 ...
- Java中的关键字synchronized
1. 介绍 在Java并发系列的文章中,这个是第二篇文章.在前面的一篇文章中,我们学习了Java中的Executor池和Excutors的各种类别. 在这篇文章中,我们会学习synchronized关 ...
- Flags Over Objects
The Flags Over Objects anti-pattern occurs when behavior is written outside of an object by inspecti ...
- 关于 https的SNI问题
遇到的问题,服务器多站点配置HTTPS 后遇到的问题,服务器报警告错误. 随后网上搜索了下 SNI的意义. 这句话很经典: SNI(Server Name Indication)是为了解决一个服务器使 ...
- 你必须知道的Docker镜像仓库的搭建
近期工作中发现用到的容器镜像越来越多(不多的时候没考虑过镜像仓库的问题),同一个容器镜像也存在多个版本,那么镜像仓库的搭建需求就涌现出来,本文就目前的几个常用镜像仓库的搭建进行介绍,我们可以根据需要选 ...
- NOIP2015斗地主题解 7.30考试
问题 B: NOIP2015 斗地主 时间限制: 3 Sec 内存限制: 1024 MB 题目描述 牛牛最近迷上了一种叫斗地主的扑克游戏.斗地主是一种使用黑桃.红心.梅花.方片的A到K加上大小王的共 ...