首先我们来介绍一下JVM中的内存区域
方法区:在java的虚拟机中有一块专门用来存放已经加载的类信息、常量、静态变量以 及方法代码的内存区域,叫做方法区。
常量池:常量池是方法区的一部分,主要用来存放常量和类中的符号引用等信息。
堆区:用于存放类的对象实例。
栈区:也叫java虚拟机栈,是由一个一个的栈帧组成的后进先出的栈式结构,栈桢中存放方法运行时产生的局部变量、方法出口等信息。当调用一个方法时,虚拟机栈中就会创建一个栈帧存放这些数据,当方法调用完成时,栈帧消失,如果方法中调用了其他方法,则继续在栈顶创建新的栈桢。
类的生命周期
类的加载
类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构,最终产品是位于运行时数据区的的堆区的CLASS文件,其封装了方法区内的数据结构,并提供了java程序提供了访问类在方法区内的数据接口。
类的加载是由JAVA类加载器完成。
连接Linking
1验证:当一个类被加载之后,必须要验证一下这个类是否合法,比如这个类是不是符合字节码的格式、变量与方法是不是有重复、数据类型是不是有效、继承与实现是否合乎标准等等。总之,这个阶段的目的就是保证加载的类是能够被jvm所运行。
2准备:准备阶段的工作就是为类的静态变量分配内存并设为jvm默认的初值,对于非静态的变量,则不会为它们分配内存。有一点需要注意,这时候,静态变量的初值为jvm默认的初值,而不是我们在程序中设定的初值。jvm默认的初值是这样的:
◦基本类型(int、long、short、char、byte、boolean、float、double)的默认值为0。
◦引用类型的默认值为null。
◦常量的默认值为我们程序中设定的值,比如我们在程序中定义final static int a = 100,则准备阶段中a的初值就是100。
3 解析:这一阶段的任务就是把常量池中的符号引用转换为直接引用。
符号引用就是一组符号来描述目标,可以是任何字面量
直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄
类的初始化
在类的生命周期执行完加载和连接之后就开始了类的初始化。
在类的初始化阶段,java虚拟机执行类的初始化语句,为类的静态变量赋值。
java类中对类变量指定初始值有两种方式:
1、声明类变量时指定初始值;2、使用静态初始化块为类变量指定初始值。
JVM初始化步骤
1、假如这个类还没有被加载和连接,则程序先加载并连接该类
2、假如该类的直接父类还没有被初始化,则先初始化其直接父类
3、假如类中有初始化语句,则系统依次执行这些初始化语句
类初始化时机
Java程序对类的使用方式可以分为两种:
1.主动使用
2.被动使用
只有当对类的主动使用的时候才会导致类的初始化,类的主动使用包括以下六种:
除了以上六种情况,其他使用Java类的方式都被看作是对类的被动使用,都不会导致类的初始化。
1.父类静态代码块;
2.子类静态代码块;
3.父类非静态代码块;
4.父类构造函数;
5.子类非静态代码块;
6.子类构造函数;
结束生命周期
在如下几种情况下,Java虚拟机将结束生命周期
– 执行了System.exit()方法
– 程序正常执行结束
– 程序在执行过程中遇到了异常或错误而异常终止
– 由于操作系统出现错误而导致Java虚拟机进程终止
JVM类加载机制
·全盘负责,当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
·父类委托,先让父类加载器试图加载该类,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
·缓存机制,缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区寻找该Class,只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区。这就是为什么修改了Class后,必须重启JVM,程序的修改才会生效。
双亲委派模型的工作流程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。
阿里的面试还是比较注重底层的实现原理的,看来阿里不是随便就能进去的。怎么样你觉得你合格吗?喜欢的朋友可以关注我哦。
学习Java的同学注意了!!!学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入Java学习交流群346942462,我们一起学Java!