1.类装载,连接,初始化
java虚拟机通过装载,连接,初始化一个java类,使得该类可以被正在运行的java程序所使用。其中,装载就是把二进制形式的类型数据合并到虚拟机的运行时状态中,连接分为三个阶段-验证,准备,解析。验证确保了java类型数据格式正确并且适用于java虚拟机使用。准备则负责为该类型分配他所需要的内存,比如为他的类变量分配内存,解析则负责把常量池中的符号引用转化成直接引用。虚拟机的实现可以推迟解析这一步,它可以在当运行中的程序真正使用某个符号的时候再去解析它。当验证、准备和可选的解析都完成了以后,该类就为初始化做好了准备,在初始化期间,会给类变量赋以适当的初始值。
类的装载,连接,初始化需要顺序的执行,唯一的例外就是解析,他可以在初始化以后再行。
java虚拟机严格定义了类的初始化时机,所有的java虚拟机必须实现:必须在每个类或者接口首次主动使用时初始化,主要是以下六种情况:
(1)当创建某个类的新实例时(包括:new(),clone(),反射,反序列化)
(2)当调用某个类的静态方法(在字节码中执行invoke static的时候)
(3)使用某个类或者接口的静态字段,或者对该字段赋值时(在字节码中执行getstatic或者putstatic指令时),用final修饰的静态常量表达式除外
(4)当调用某些反射方法的时候,比如:Class中的方法,或者java.lang.reflect包中的类的方法。
(5)当初始化某个类的子类(一个类初始化的时候要求他的父类已经都被初始化完了)
(6)当虚拟机启动时,被标明为启动类的类(包含main方法的类,也是一个静态方法)
除了以上六种情况外,其他的所有的对类的使用都是被动的使用,都不会导致类的初始化。
对于任何一个类的初始化,都要求它的超类在它初始化之前就已经完成了初始化,但是,对于接口而言,这条规则并不适用。只有在某个接口所声明的非常量字段被使用时,该接口才会被初始化,它不会因为实现这个接口的子接口或者子类的初始化而初始化。(其实也好理解,接口字段都是static final类型,有的是编译时常量,只有对非常量的引用才会导致初始化)
1.1装载
装载分为三个基本的动作,要装在一个类型,java虚拟机必须要:
(1)通过该类型的完全限定名,产生一个代表该类型的二进制数据流
(2)解析这个二进制数据流为方法区内的内部数据结构
(3)创建一个表示该类型的java.lang.Class类对象
类装载步骤的最终产品就是这个Class对象,它是java程序和内部数据结构之间的接口。
类装载器并不需要一直等到某个类首次主动使用才会去载入它,java虚拟机规范允许类装载器缓存java类型的二进制表现形式,在预料到某个类型可能会被使用的时候就可以预先装载它。如果类装载器在预先装载时遇到缺失或者错误的class文件,它必须要等到程序首次主动使用这个类的时候才可以报错。如果这个类一直没有被主动使用,那么累装载器就一直不会报错。
1.2连接-验证
当装在完成以后,接下来就是连接。连接的第一个步骤就是验证:确认类型符合java的语义,且不会危及虚拟机的完整性。在验证的时候不同的虚拟机有很大的灵活性。
但是一般来说,首先要确保个各类之间的二进制兼容性:
(1)检查final类不能有子类
(2)检查final方法不能被覆盖
(3)确保类和超类之间没有不兼容的方法声明(比如:两个方法一摸一样,返回却不一样)
载入一个类要求他的超类都已经被载入,包括接口,初始化一个类要求他的父类都已经初始化了,但是接口不要求初始化一个子接口的时候,要初始化它的父接口,只是载入了,并不要求初始化。
在验证期间,这个类和他的所有的超类相互之间要二进制兼容,包括:
(1)检查所有的常量池入口相互之间的一致
(2)检查常量池中所有的特殊字符串(类名,字段名,方法名,字段描述符,方法描述符)是否符合格式
(3)检查字节码的完整性
1.3连接-准备
虚拟机载入了一个类,执行一些验证以后就进入了准备阶段。在这个阶段,java虚拟机会为类变量分配内存,设置默认的初始值。但在到达初始化阶段之前,类变量都没有被初始化为真正的初始值,准备阶段是不会执行java代码的。基本类型的默认值为0,boolean为false,引用类型为null。
1.4连接-解析
解析就是在类的常量池中寻找类,接口,字段和方法的符号引用,然后把这些符号引用替换成直接引用。
1.5初始化
为类变量赋以正确的初始值。
在java中,一个正确的初始值是类变量的初始化语句或者静态代码块语句给出的。
class Examplela{//类变量的初始化语句
static int size=3*(int)(Math.random()*5.0);
}
class Examplelb{//静态代码块
static int size;
static{
size=3*(int)(Math.random()*5.0);
}
}
所有的类变量初始化语句和静态代码块都被java编译器收集到一起,放到一个特殊的方法中,对于类来说,这个方法叫做类的初始化方法,对于接口来说,被成为接口初始化方法。在类和接口的java class文件当中,这个方法叫做<clinit>。通常的java程序无法调用这个<clinit>方法,只是被虚拟机来调用,专门用来把静态变量设置为他们的正确的值。
初始化一个类包含两个步骤:
(1)如果类有超类,且还没有初始化,先初始化超类
(2)如果类存在初始化方法就执行这个方法。
而初始化接口并不需要初始化它的父接口,因此只需要看看是否有接口初始化方法,有就执行。
<clinit>()方法:
class Examplelc{
static int width;
static int height=(int)(Math.random()*2.0);
static{
width=3*(int)(Math.random()*5.0);
}
}
<clinit>()方法首先执行Examplelc的唯一的一个类变量初始化代码,初始化了height,然后执行静态代码块,初始化了width,初始化是按照他们在源代码中出现的顺序来进行的。
并非所有的类都需要在他们的class文件中拥有一个<clinit>()方法,如果类没有声明任何的类变量,也没有任何的静态初始化块,就不会有<clinit>()方法。即使有类变量,但是并没有明确的使用类变量初始化语句或者静态代码块来初始化他们,那么类也不会有<clinit>()方法。只有static final常量表达式的也不会有<clinit>()方法。
下面是一个同时使用常量和其他类变量的例子:
class Exampleld{
static final int angle=35;
static final int length=angle*2;
}
Exampleld类被装载的时候,angle和length并没有作为类变量保存在方法区内,他们是常量,不是类变量,java虚拟机在使用它们的任何类的常量池或者字节码中直接存放的是他们的字面值。
class Examplele{
static int symbolicRef = Examplela.size;
static int localConst=Exampleld.length*(int)(Math.random()*3.0);//直接在字节码中嵌入70,而不存在符号引用
}
接口也可能在class文件中包含<clinit>()方法。接口当中的字段都是public static final,而且必须要在字段的初始化语句中进行初始化,如果接口中的字段不能被解析成一个编译时常量,接口就会拥有一个<clinit>()方法:
interface Examplele{
int ketchup=0;
int mustard=(int)(Math.random()*5.0);
}
关于方法区
类的初始化:
java虚拟机严格定义了类的初始化时机,所有的java虚拟机必须实现:必须在每个类或者接口首次主动使用时初始化,主要是以下六种情况:
(1)当创建某个类的新实例时(包括:new(),clone(),反射,反序列化)
(2)当调用某个类的静态方法(在字节码中执行invoke static的时候)
(3)使用某个类或者接口的静态字段,或者对该字段赋值时(在字节码中执行getstatic或者putstatic指令时),用final修饰的静态常量表达式除外
(4)当调用某些反射方法的时候,比如:Class中的方法,或者java.lang.reflect包中的类的方法。
(5)当初始化某个类的子类(一个类初始化的时候要求他的父类已经都被初始化完了)
(6)当虚拟机启动时,被标明为启动类的类(包含main方法的类,也是一个静态方法)
除了以上六种情况外,其他的所有的对类的使用都是被动的使用,都不会导致类的初始化。
类的卸载:
方法区即后文提到的永久代,很多人认为永久代是没有GC的,《Java虚拟机规范》中确实说过可以不要求虚拟机在这区实现GC,而且这区GC的“性价比”一般比较低:在堆中,尤其是在新生代,常规应用进行一次GC可以一般可以回收70%~95%的空间,而永久代的GC效率远小于此。虽然VM Spec不要求,但当前生产中的商业JVM都有实现永久代的GC,主要回收两部分内容:废弃常量与无用类。这两点回收思想与Java堆中的对象回收很类似,都是搜索是否存在引用,常量的相对很简单,与对象类似的判定即可。而类的回收则比较苛刻,需要满足下面3个条件:
1.该类所有的实例都已经被GC,也就是JVM中不存在该Class的任何实例。
2.加载该类的ClassLoader已经被GC。
3.该类对应的java.lang.Class 对象没有在任何地方被引用,如不能在任何地方通过反射访问该类的方法。
是否对类进行回收可使用-XX:+ClassUnloading参数进行控制,还可以使用-verbose:class或者-XX:+TraceClassLoading、-XX:+TraceClassUnLoading查看类加载、卸载信息。
相关推荐
Hibernate对象的生命周期 Hibernate对象的生命周期 Hibernate对象的生命周期
1. **对象生命周期的开始**: - 当对象被创建时,其生命周期开始。首先需要为对象分配内存空间,在Java堆内存中进行。 - 接着,对象的实例变量会被初始化为其默认值或指定的初始值。 - 对象可以通过多种方式创建...
在Java虚拟机(JVM)中,对象的生命周期包含了多个阶段,这些阶段共同决定了一个对象从诞生到消亡的过程。以下是这些阶段的详细介绍: **创建阶段(Creation)** 在这个阶段,对象从无到有,主要经历以下几个步骤:...
总之,C#中的对象生命周期管理通过CLR和垃圾回收器提供了一套自动化的内存管理机制。通过学习本章内容,开发者将能够理解并掌握如何在C#中创建和管理对象,以及如何通过各种方式正确地处理资源释放问题,以编写出...
9. **生命周期方法**:在某些编程语言和框架中,领域对象可能包含一些生命周期方法,如 `save()`、`delete()`、`load()` 等,用于操作对象的生命周期。 10. **CQRS(命令查询责任分离)**:在复杂系统中,CQRS 模式...
综上所述,领域对象的生命周期管理是软件开发中至关重要的一部分,它涉及到对象的创建、使用、状态管理、持久化、查询和销毁等多个环节。理解和掌握这些知识点对于构建高效、稳定且符合业务需求的系统至关重要。在...
本文将详细探讨“测试实体对象的生命周期”,并结合给定的标签“源码”和“工具”,阐述如何通过Hibernate Session来理解和控制这些对象的状态。 首先,理解实体对象的生命周期对于数据库操作至关重要。在Hibernate...
在Java中,对象可以通过多种方式创建,这些创建过程标志着对象生命周期的开始。本章节详细介绍了四种常见的创建对象的方法: 1. **使用`new`语句创建对象**:这是创建Java对象最常见的方法。通过这种方式,我们可以...
在这个周期内,JVM负责管理所有类和对象的生命周期。 **2. Java程序与JVM生命周期的一致性** 程序生命周期与Java虚拟机生命周期是一致的。这意味着Java虚拟机进程从创建起的任务就是执行Java程序,直至程序正常...
这个生命周期涉及多个阶段,对于理解和优化ASP.NET应用程序的性能至关重要。 首先,当一个HTTP请求到达Web服务器(通常是IIS)时,服务器会根据请求的文件扩展名决定哪个ISAPI(Internet Server API)扩展来处理...
总的来说,理解Hibernate中持久化对象的生命周期是优化数据库操作的关键,它直接影响到对象与数据库之间的交互效率和数据的一致性。掌握这些概念可以帮助开发者编写出更加高效、健壮的Java应用。
总结起来,Activity生命周期管理是Android开发中的关键技能,通过合理使用`ActivityLifeCallbackManagers`和`ActivityLifeCallbackManager`这样的工具类,可以更高效、准确地控制和监控Activity的状态,从而提高应用...
CORBA 对象生命周期之实现和内存管理 Java 教程 CORBA(Common Object Request Broker Architecture)是一种分布式对象架构,允许不同语言编写的对象之间进行通信。CORBA 对象生命周期是指对象从创建到销毁的整个...
UIView的生命周期对于理解iOS应用中视图的加载和管理至关重要。在开发iOS应用时,了解UIView及其子类的生命周期方法,可以让开发者合理地安排资源的分配和释放,优化应用的性能,以及提供更好的用户体验。 首先,...
总之,掌握TAO对象生命周期服务不仅能够帮助我们更好地理解和应用CORBA规范,还能够提高我们在分布式系统设计中的专业能力。通过实际项目的实践,我们可以更好地掌握这些概念,并将其运用到更复杂的系统中,提升软件...
在实际开发中,了解对象生命周期管理并合理利用,如选择合适的垃圾收集器,控制对象的创建和存活,可以帮助提升应用程序的性能。例如,使用`WeakReference`或`SoftReference`来弱化对象引用,可以使得垃圾收集器更...
在Android应用开发中,Activity和Fragment是两个至关重要的组件,它们构成了用户界面的主要部分,并各自具有独特的生命周期。了解和掌握这两个组件的生命周期是每个Android开发者必须具备的基础技能。 首先,我们来...
对于iPhone开发来说,掌握这个生命周期有助于编写更加高效和响应良好的应用。 首先,让我们回顾一下Objective-C中的协议(Protocol)。协议在iOS开发中扮演着接口的角色,允许类之间定义一套可选或必需的方法。在...