Class的生命周期

之前的《JVM类加载机制-ClassLoader》和《初探JVM-ClassLoader源码》,只是讨论了Class的加载部分,现在来纵观一下整个Class的生命周期。

Class的生命周期就是指一个class文件(字节码)从加载到卸载的全过程。

当一个类被装载、连接、初始化后,它的生命周期就开始了,当代表该类的Class对象不再被引用、即已经不可触及的时候,Class对象的生命周期结束。那么该类的方法区内的数据也会被卸载,从而结束该类的生命周期。

一个类的生命周期取决于它Class对象的生命周期,经历加载、连接、初始化、使用、和卸载五个阶段。

类的加载(Load Class)包含了以下三个步骤:

装载 Loading

查找Class的二进制文件(.class),把类的信息加载到JVM的方法区中,对其进行部分检验(类文件的魔数,文件长度,是否有父类等);在堆区中实例化一个java.lang.Class对象,作为方法区中这个类的信息的入口。

加载方式有多种:A. 在classPath下找相应class文件;B. 从jar文件中读取;C. 从网络中获取;D. 实时生成,如设计模式中的动态代理模式;E. 从非class文件中获取,这些文件在jvm中运行之前也会被转换为可识别的字节码文件。

一般来说加载和连接是同步的,但有时候也会交叉进行,但是两者的开始和结束是顺序的。

链接 Linking

将对应的字节码文件读入到JVM中(其中解析步骤是可以选择的)

a)        验证

检查载入的class文件数据的合法性:字节码的格式,变量与方法是否重复、数据类型是否有效、继承与实现是否合法,接入属性是否正确(public ,private的问题),检查 final class 有没有被继承,检查静态变量的正确性等等。

b)        准备

给类的静态变量分配存储空间:赋default值(基本类型为0,引用为null),静态常量(如final static int a = 100),但不会对其进行初始化,不会执行任何 Java 代码(static块)。

c)        解析

将符号引用转成直接引用,完成内存结构的布局。

例如我们要调用Collections.toString(),java.util.Collections.toString()就是符号引用,而直接引用就是这个方法在方法区中的内存地址。解析就是把类、接口、方法、成员变量的符号引用转换成内存地址,以供调用。

在连接阶段完成后,再根据使用的情况(直接引用or被动引用)来决定是否类进行初始化。

初始化 Initializing

对静态变量赋assign值,执行静态代码块。   【类的初始化顺序可以参考我的《类的初始化&实例化顺序》】

在Java中类的引用分为直接引用和被动引用,只有直接引用,会触发类的初始化:

a) new实例化对象;b) 使用类的(非常量)静态变量 / 方法;c) 通过反射执行前三种情况;d) 子类被初始化;e) 作为程序入口,调用main(也是调用静态方法的一种)。

其他使用类的方式都叫被动引用,如:

a) 定义类数组;b) 引用类的静态常量;c) 引用父类的静态域,只会触发父类的初始化,而不会触发子类的初始化。

从上面能看出,初始化的原则是,只初始化要用到的。在主动引用中,实例化(new/main)、使用静态域都是被用到了;而子类依赖了父类,所以也会触发初始化。在被动引用中,a中的类型只是用于编译器的校验,而b中的静态常量是在链接中的准备阶段已经完成了,所以两者都用不上去初始化;另外c中只用到了父类的静态域,所以就没必要触发子类。

public class LoaderLazy {
    publicstatic String HELLO = "Hello";
    static{
       System.out.println("Init parent");
    }
class SubLoaderLazy extends LoaderLazy{
    static{
       System.out.println("Init sub");
    }
}
    System.out.println(SubLoaderLazy.HELLO);

console:

Init parent

能看出上面只初始化了父类。那如果我们把HELLO属性放在子类SubLoaderLazy中,子类也会被初始化。

console:

Init parent

Init sub

实例化 NewInstance

类的初始化完成后,我们就可以创建对象实例了。

相比初始化只执行类的静态域(静态变量/代码块),类的实例化是执行类的实例域(非静态变量/代码块)和构造函数,并且在堆区创建一个类的实例对象。

(在实例化时,如果没有指定构造函数,JVM会自动构造一个无参构造函数。)

类的实例化,一般在使用new创建对象,或者Class.newInstance()时执行。

【类的实例化顺序可以参考我的《类的初始化&实例化顺序》】

回收卸载 GC

Class作为JVM中的一个特殊对象,也会被GC回收卸载。

Class的卸载就是清空方法区中Class的信息和堆区中的java.lang.Class对象。这时Class的声明周期就结束了。

SUN的原话:“class or interface may be unloaded if andonly if its class loader is unreachable. Classesloaded by the BootstrapClassLoadermay not be unloaded”。

Class被回收要满足以下三个条件:

a)        No Instance:该类所有的实例都已经被GC;

b)        No ClassLoader:加载该类的ClassLoader实例已经被GC;

c)        No Reference:该类的java.lang.Class对象没有被引用。(XXX.class, 静态变量/方法)

另外,有些Class是不会被回收的:

1、根据JVM和JLS的规范,由Bootstrap类加载器加载的Class在整个运行期间是不会被卸载的。

2、被Extension类加载器 和System类加载器加载的Class在运行期间不太可能被卸载,因为这些实例基本上在整个运行期间总能直接或者间接的访问的到,其达到unreachable的可能性极小。

3、开发者自定义的类加载器加载的Class只有在很简单的上下文环境中才能被卸载,稍微复杂点的应用场景中,很难有符合上面3个条件的Class。

综合以上三点,一个已经加载的Class被卸载的几率很小;而且卸载时间也是不可控的。而且Class占用的内存空间不大,一般不用考虑。

参考博客 http://blog.csdn.net/biaobiaoqi/article/details/6909141

时间: 2024-07-30 03:14:40

Class的生命周期的相关文章

iOS程序执行顺序和UIViewController 的生命周期(整理)

说明:此文是自己的总结笔记,主要参考: iOS程序的启动执行顺序 AppDelegate 及 UIViewController 的生命周期 UIView的生命周期 言叶之庭.jpeg 一. iOS程序的启动执行顺序 程序启动顺序图 iOS启动原理图.png 具体执行流程 程序入口进入main函数,设置AppDelegate称为函数的代理 程序完成加载[AppDelegate application:didFinishLaunchingWithOptions:] 创建window窗口 程序被激活[

android Activity 的生命周期 以及横屏竖屏切换时 Activity 的状态变化

生命周期Android 系统在Activity 生命周期中加入一些钩子,我们可以在这些系统预留的钩子中做一些事情.例举了 7 个常用的钩子:protected void onCreate(Bundle savedInstanceState)protected void onStart()protected void onResume()protected void onPause()protected void onStop()protected void onRestart()protecte

连载《一个程序猿的生命周期》-《发展篇》- 12.向生活妥协的选择之路,你也面临吗?

本篇文章的主角是第二个加入我们团队的,暂且称他为G兄.是我第二家公司的同事,但是当时并没有交集,后来经过其他同事说起,被我招过来的.关于第二家公司的情况,请参见<而立之年,第一次跳槽,寻求转型> 在加入我们团队之前,G兄在一个不大不小的公司做内部OA系统,众所周知不会有什么太大发展,他当时也不太满意.在和他交流的过程中,我说的很直接:1.开发公司内部OA,并非公司实际产品,无法直接创造利润,就算是公司的产品,现在做OA的多了去了.2.OA开发完成后,只剩运维人员,假设裁掉一部分人员的话,你怎么

【Vue】详解Vue生命周期

Vue实例的生命周期全过程(图) (这里的红边圆角矩形内的都是对应的Vue实例的钩子函数) 在beforeCreate和created钩子函数间的生命周期 在beforeCreate和created之间,进行数据观测(data observer) ,也就是在这个时候开始监控data中的数据变化了,同时初始化事件 created钩子函数和beforeMount间的生命周期 对于created钩子函数和beforeMount间可能会让人感到有些迷惑,下面我就来解释一下: el选项的有无对生命周期过程

vue 生命周期初探

vue 以后发之势加上其独有的特性(压缩后很小),轻量级的MVVM框架,目前github star已有5.94万,而react 7万.由此可见是两个非常热门前端框架.这里就vue的生命周期做个初步体验. 发现看视频,动手之后,过段时间还是会忘,所以写一篇短文以备不时之需. 先附上官网的图片:vue生命周期 生命周期的钩子函数如果使用得当,会大大增加开发效率: 生命周期实践: 为了更好的查看beforeUpdate.updated,beforeDestroy,destroy钩子函数,使用v-on绑

1.2软件生命周期&amp;测试流程

软件的生命周期 可行性分析-需求分析-软件设计-软件编码-软件测试-软件维护 1.可行性分析 主要确定软件开发的目的和可行性(PM) 2.需求分析 对软件的功能进行详细的分析(PM),输出需求规格说明书(原型图) 3.软件设计(DEV) 把需求分析得到的结果转换为软件结构和数据结构,形成系统架构 概要设计:搭建架构.模块功能.接口连接和数据传输 详细设计:模块深入分析,对各模块组合进行分析,伪代码   包含数据库设计说明 4.软件编码(DEV) 可运行的程序代码 5.软件测试 5.1.单元测试(

ASP.NET页面生命周期与控件生命周期

ASP.NET页面生命周期 (1)PreInit 预初始化(2)Init 初始化(3)InitComplete 初始化完成(4)PreLoad 预加载(5)Load 加载(6)LoadComplete 加载完成(7)PreRender 预输出(8)PreRenderComplete 预输出完成(9)Unload 卸载 ASP.NET控件生命周期 -- 实例化(Instantiate) 控件被页面或另一个控件通过调用它的构造器所实例化.这个步骤之后所列出的阶段,仅当控件加入控件树中才会发生. --

Servlet简介与生命周期

转载请注明原文地址: 一:Servlet是什么 Servlet是运行在Web服务器上的Java程序,作为处理来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层.JSP在web服务器上要先转换成servlet,然后才能在JVM运行,并把结果拼接成浏览器可识别的文件(如html)传回浏览器显示. 二:Servlet的应用场景 单纯地对客户端的请求做处理时,如果我们用纯JSP文件(即:只有Java语句)来处理的话,还需要先转换为servlet才能运行

Servlet的生命周期

Servlet的生命周期是由Servlet的容器来控制的,它可以分为三个阶段:初始化.运行.销毁1.初始化阶段:(1)Servlet容器加载Servlet类,把Servlet类的.class文件中数据读到内存中:(2)然后Servlet容器创建一个ServletConfig对象.ServletConfig对象包含了Servlet的初始化配置信息:(3)Servlet容器创建一个Servlet对象:(4)Servlet容器调用Servlet对象的init方法进行初始化.2.运行阶段当Servlet

第一章:Activity的生命周期和启动模式

Activity是Android中四大组件之首,所以需要重视,尤其是启动方式,在AndroidManifest.xml中的注册信息 (一)Activity的生命周期 1.1.1 正常情况下的生命周期 说明 (1)针对一个特定的Activity,第一次启动顺序:onCreate->onStart->onResume. (2)当用户打开新的Activity或者切换到桌面的时候,回调如下:onPause->onStop (3)返回原Activity时,回调如下:onRestart->on