注册 登录
  • 欢迎来到幻月小筑

方法区和类的加载

深入理解jvm seal 14次浏览 0个评论

学习jvm的时候,老是记混了几个java内存区域中的东西,通过方法区和class文件加载过程,看看这两个的联系

一、方法区

方法区在JVM中也是一个非常重要的区域,它与堆一样,是被线程共享的区域。在方法区中,存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等。

在Class文件中除了类的字段、方法、接口等描述信息外,还有一项信息是常量池,用来存储编译期间生成的字面量和符号引用。

在方法区中有一个非常重要的部分就是运行时常量池,它是每一个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来。当然并非Class文件常量池中的内容才能进入运行时常量池,在运行期间也可将新的常量放入运行时常量池中,比如String的intern方法。

在JVM规范中,没有强制要求方法区必须实现垃圾回收。很多人习惯将方法区称为“永久代”,是因为HotSpot虚拟机以永久代来实现方法区,从而JVM的垃圾收集器可以像管理堆区一样管理这部分区域,从而不需要专门为这部分设计垃圾回收机制。不过自从JDK7之后,Hotspot虚拟机便将运行时常量池从永久代移除了。jdk8中添加Metaspace取代了Perm

 

二、class文件加载过程

过程:加载—–>验证——>准备——>解析——>初始化

加载 发生的变化:

(1)、根据类的全名限定符,获取class二进制流(保存到硬盘的class文件,不是class二进制流获取的唯一方式。也有可能从网络中获取等)

(2)、将类的静态存储结构转化为方法区的运行时动态存储结构

(3)、在内存的堆中生成对应的class对象,作为方法区的入口

验证要做的事情:

(1)、class文件格式验证(class文件来源不唯一)

(2)、元数据验证(是否符合类的定义规范,例如是否继承java.lang.Object)

(3)、字节码验证(类中方法的控制流是否合法)

(4)、符号引用验证(转换为直接引用动作是否合法)

准备发生的事情:为类变量在方法区分配内存,并初始化类变量(“零值”初始化)

解析要干的事情:将常量池的符号引用替换为直接引用

初始化:在准备阶段已经对类变量进行初始化了,这里的初始化是执行类构造器<clinit>。<clinit>()方法是编译器自动收集类中所有类变量的赋值动作和静态代码块而产生的方法(无论类变量和静态代码块的位置是什么样,都是先执行类变量的赋值动作,再执行静态代码块)

初始化触发的条件,有且只有四个(主动引用)

(1)、new(实例化对象)、getstatic(获取类变量的值,被final修饰的除外,他的值在编译器时放到了常量池)、putstatic(给类变量赋值)、invokestatic(调用静态方法)

(2)、使用java.lang.reflect包的方法对类进行反射调用方法

(3)、初始化类的时候,如果他的父类还没有初始化,要先初始化父类

(4)、虚拟机启动时,含有main方法的类,会被先初始化

被动引用(除了上面引用类的四个条件会触发类的初始化,其他对类的引用都不会触发类的初始化):

(1)、在第三方类中,使用子类引用父类的类变量,不会初始化子类

(2)、在第三方类中,通过数组定义来应用类,不会初始化类

(3)、在第三方类中,引用类的常类变量(同时被final和static修饰的变量),不会触发类的初始化(因为在第三方类的编译之后,常量就被放在第三方类的常量池中了)


幻月小筑丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明方法区和类的加载
喜欢 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到