类加载

在java代码中,类型的加载,连接与初始化过程都是在程序运行期间完成的

类型:表示的Object本身,并不是指一个对象,也就是class.

运行期间:表示的是一种runtime的概念,在运行期间完成就可以提供更大的灵活性,增加了更多的可能性

java虚拟机与程序的生命周期

在如下几种情况下,java虚拟机将结束生命周期:

  1. 执行了System.exit()方法
  2. 程序正常执行结束
  3. 程序在执行过程中遇到了异常或错误而异常终止
  4. 由于操作系统出现错误而导致java虚拟机进程终止
类的加载,连接与初始化

加载:查找并加载类的二进制数据

连接:

  • 验证:确保被加载的类的正确性
  • 准备:为类的静态变量分配内存,并将其初始化默认值
  • 解析:把类中的符号引用转换成直接引用

初始化:为类的静态变量赋予正确地初始值

java程序对类的使用方式可分为两种

  • 主动使用
  • 被动使用

    所有的java虚拟机实现必须在每个类或接口被java程序"首次主动使用"时才初始化他们

主动使用:

  • 创建类的实例
  • 访问某个类或接口的静态变量,或者对该静态变量赋值(getstatic, putstatic)
  • 调用类的静态方法(invokestatic)
  • 反射(如Class.forName("com.test.Test"))
  • 初始化一个类的子类
  • java虚拟机启动时被标明为启动类的类(包含main方法的类 )
  • jdk1.7开始提供的动态语言支持:

    java.lang.invoke.MethodHandle实例的解析结果REF_getStatic, REF_putStatic,REF_invokeStatic句柄对应的类没有初始化,则初始化

除了以上七种情况,其他使用java类的方式都被看作是对类的被动使用,都不会导致类的初始化

类的加载:

类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在内存中创建一个java.lang.Class对象(规范并未说明Class对象位于哪里,HotSpot虚拟机将其放在了方法区中)用来封装类在方法区内的数据结构

加载.class文件的方式:

  • 从本地系统中直接加载
  • 通过网络下载.class文件
  • 从zip,jar等归档文件中加载.class文件
  • 从专门的数据库中提取.class文件
  • 将java源文件动态编译为.class文件(如:动态代理,jsp转化成java类)
public class Mytest1 {

    public static void main(String[] args) {
/**
* MyParent1 static block
* hello world
* 对于静态字段来说,只有直接定义了该字段的类才会被初始化
* 只对父类主动使用了, 所以只加载了父类
*/
//System.out.println(MyChild1.str); /**
* MyParent1 static block
* MyChild1 static block
* welcome
* 初始化一个类的子类的时候,父类也会被初始化
*/
System.out.println(MyChild1.str2);
}
} class MyParent1 { public static String str = "hello world"; static {
System.out.println("MyParent1 static block");
}
} class MyChild1 extends MyParent1 { public static String str2 = "welcome";
static {
System.out.println("MyChild1 static block");
}
}

例子2

package jvm.classloader;

/**
* 这里我们用javap -c MyTest2 反编译之后可以看到
* 几个助记符
* ldc表示int,float或是String类型的常量值从常量池中推送至栈顶
* bipush表示将单字节(-128 ~ 127) 的常量值推送至栈顶
* sipush表示将一个短整型常量值(-32768 ~ 32767)推送至栈顶
* iconst_1表示将int类型1推送至栈顶
* iconst 最多到5, 专门为-1~5提供了助记符(iconst_m1~iconst_5)
*
*/
public class MyTest2 {
public static void main(String[] args) {
/**
* hello world
* 如果str变量不加final则会把静态代码块打印出来
* 因为加了final就表示常量,所以在编译阶段就会被存入调用这个方法所在
* 的类的常量池中,本质上,调用类并没有直接饮用到定义常量类,因此并不会
* 触发定义常量的类的初始化
* 注意: 这里指的是将常量存放到了MyTest2的常量池中,之后MyTest2与
* MyParent2就没任何关系了
* 甚至,我们可以将MyParent2的class文件删除
*/
System.out.println(MyParent2.i);
}
} class MyParent2 { public static final String str = "hello world";
public static final short s = 127;
public static final int i = 128;
public static final int m= 1; static {
System.out.println("MyParent2 static block");
}
}

例子3

public class Mytest3 {
public static void main(String[] args) {
/**
* 对于一个这样的必须在运行期间才能知道的值的话,把这个值放到常量池里面是没什么意义的
* 所以就不会放到常量池中.这时在程序运行时,会导致主动使用这个常量所在的类,
* 显然会导致这个类被初始化
*/
System.out.println(MyParent3.str);
}
} class MyParent3 { public static final String str = UUID.randomUUID().toString(); static {
System.out.println("MyParent3 static code");
}
}

例子4

/**
* 助记符:
* anewarray:表示创建一个引用类型的(如类,接口,数据)数据,并将其引用值压入栈顶
* newarray:表示创建一个指定的原始类型(如:int,float,char等)的数组,并将其引用值压入栈顶
*/
public class MyTest4 { public static void main(String[] args) {
/**
* 创建类的对象属于主动使用,所以会导致类的初始化
*/
//MyParent4 myParent4 = new MyParent4(); /**
* 这里类不会被初始化
* 对于数组实例来说,其类型是由JVM在运行期间动态生成的,表示为[Ljvm.classloader.MyParent4
* 这种形式.动态生成的类型,其父类型就是Object.
* 对于数组来说,JavaDoc经常将构成数组的元素为Component,实际上就输出将数组降低一个维度后的类型
*
*/
MyParent4[] myParent4s = new MyParent4[1]; System.out.println(myParent4s.getClass());
System.out.println(myParent4s.getClass().getSuperclass());
/**
* [I
* java.lang.Object
*/
int[] ints = new int[1];
System.out.println(ints.getClass());
System.out.println(ints.getClass().getSuperclass());
}
} class MyParent4 { static {
System.out.println("Myparent4 static block");
}
}

例子5

/**
* 当一个接口在初始化时,并不要求其父接口都完成了初始化
* 只有在真正使用到父接口的时候(如引用接口中所定义的常量时),才会初始化
*/
public class MyTest5 {
public static void main(String[] args) {
System.out.println(MyChild5.b);
}
} class MyParent5 {
//public static int a = 5;
public static int a = new Random().nextInt(5);
} class MyChild5 extends MyParent5 {
public static int b = 6;
//public static int b = new Random().nextInt(4);
}

例子6

public class MyTest6 {
public static void main(String[] args) { Singleton instance = Singleton.getInstance();
/**
* 1:
* counter1: 1
* counter2: 1
*
* 2:
* counter1: 1
* counter2: 0
*
* 在类的加载中,连接阶段里面有个准备阶段,在准备阶段的时候,jvm会给
* 类分配内存,并将其初始化为默认值
* 然后再进行初始化,初始化时是按照我们所申明的变量从上到下的顺序去执行
* 所以改变变量的顺序的时候执行顺序也被改变
*/
System.out.println("counter1: "+Singleton.counter1);
System.out.println("counter2: "+Singleton.counter2);
}
} class Singleton { public static int counter1;
//1
//public static int counter2=0; private static Singleton singleton = new Singleton(); private Singleton() {
counter1++;
counter2++;//如果在准备阶段没有附上默认值,那么这里是无法++的,所以准备阶段具有重要意义
}
//2
public static int counter2=0; public static Singleton getInstance() {
return singleton;
}
}

深入理解JVM-类加载器深入解析(1)的更多相关文章

  1. JVM 类加载器深入解析以及重要特性剖析

    1.类加载流程图 从磁盘加载到销毁的完整过程. 2.类加载流程图2 1.加载: 就是把二进制形式的java类型读入java虚拟机中 2.连接: 验证.准备.解析. 连接就是将已经读入到内存的类的二进制 ...

  2. 深入理解Java类加载器(一):Java类加载原理解析

    摘要: 每个开发人员对java.lang.ClassNotFoundExcetpion这个异常肯定都不陌生,这个异常背后涉及到的是Java技术体系中的类加载机制.本文简述了JVM三种预定义类加载器,即 ...

  3. 深入理解Java类加载器(ClassLoader)

    深入理解Java类加载器(ClassLoader) Java学习记录--委派模型与类加载器 关于Java类加载双亲委派机制的思考(附一道面试题) 真正理解线程上下文类加载器(多案例分析) [jvm解析 ...

  4. 深入理解Java类加载器(ClassLoader) (转)

    转自: http://blog.csdn.net/javazejian/article/details/73413292 关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Ja ...

  5. 深入理解JVM虚拟机6:深入理解JVM类加载机制

    深入理解JVM类加载机制 简述:虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 下面我们具体 ...

  6. 深入理解Java类加载器(二):线程上下文类加载器

    摘要: 博文<深入理解Java类加载器(一):Java类加载原理解析>提到的类加载器的双亲委派模型并不是一个强制性的约束模型,而是Java设计者推荐给开发者的类加载器的实现方式.在Java ...

  7. JVM类加载器的分类

    类加载器的分类 JVM支持两种类型的类加载器,分别为引导类加载器(Bootstrap ClassLoader)和自定义类加载器(User-Defined ClassLoader). 从概念上来讲,自定 ...

  8. 深入JVM类加载器机制,值得你收藏

    先来一道题,试试水平 public static void main(String[] args) { ClassLoader c1 = ClassloaderStudy.class.getClass ...

  9. 深入理解Java类加载器(1):Java类加载原理解析

    1 基本信息 每个开发人员对Java.lang.ClassNotFoundExcetpion这个异常肯定都不陌生,这背后就涉及到了java技术体系中的类加载.Java的类加载机制是技术体系中比较核心的 ...

  10. JVM 类加载器命名空间深度解析与实例分析

    一.创建Sample 1.创建实例 public class MyPerson { private MyPerson myPerson; public void setMyPerson(Object ...

随机推荐

  1. MethodInterceptor-方法拦截器

    MethodInterceptor 方法拦截器,也就是aop拦截方法 1.使用示例 public interface MethodInterceptor extends Interceptor { O ...

  2. 设计模式之策略模式和状态模式(strategy pattern & state pattern)

    本文来讲解一下两个结构比较相似的行为设计模式:策略模式和状态模式.两者单独的理解和学习都是比较直观简单的,但是实际使用的时候却并不好实践,算是易学难用的设计模式吧.这也是把两者放在一起介绍的原因,经过 ...

  3. 基于STM32之UART串口通信协议(三)接收

    一.前言 1.简介 回顾上一篇UART发送当中,已经讲解了如何实现UART的发送操作了,接下来这一篇将会继续讲解如何实现UART的接收操作. 2.UART简介 嵌入式开发中,UART串口通信协议是我们 ...

  4. 前端笔记之React(三)使用动态样式表&antd&React脚手架&props实战

    一.使用动态样式表 1.1 LESS使用 全局安装Less npm install -g less 创建1.less文件,然后可以用lessc命令来编译这个文件: lessc 1.less 1.css ...

  5. 跟我学SpringCloud | 第七篇:Spring Cloud Config 配置中心高可用和refresh

    SpringCloud系列教程 | 第七篇:Spring Cloud Config 配置中心高可用和refresh Springboot: 2.1.6.RELEASE SpringCloud: Gre ...

  6. Git 安装教程(2.21.0)

    获取安装包地址 1.官网(较慢):https://git-scm.com/downloads 2.下载链接:http://down-ww3.newasp.net/pcdown/soft/soft1/g ...

  7. c++学习书籍推荐《Exceptional C++ Style》下载

    百度云及其他网盘下载地址:点我 编辑推荐 软件“风格”所要讨论的主题是如何在开销与功能之间.优雅与可维护性之间.灵活.性与过分灵活之间寻找完美的平街点.在本书中,著名的C++大师Herb Sutter ...

  8. Spring源码阅读-ApplicationContext体系结构分析

    目录 继承层次图概览 ConfigurableApplicationContext分析 AbstractApplicationContext GenericApplicationContext Gen ...

  9. 附录:1-Grain生命周期-译注

    Grain Lifecycle Grains are logical entities that always exist, virtually, and have stable logical id ...

  10. something good

    CF292A CF304B CF383A CF409D CF409F CF632A CF652B CF656A CF656B CF656D CF659A CF678A CF697A CF735D CF ...