JVM-类型的生命周期

类型的生命周期包括:装载,连接,初始化,卸载(和类装载器有关,不一定有)。
[color=red]装载:[/color]将类的结构信息装载到内存中(这其中就包括很重要的角色--类的常量池,但是符号引用还没有解析),装载完毕之后会生成一个该类的Class对象。
[color=red]连接:[/color]连接又包括[color=red]验证,准备和解析[/color]。验证就是检验class文件语法等正确性。准备是为类变量分配内存并为他赋默认值。解析是将在装载阶段装载到内存中的常量池的符号引用解析为直接引用,这一步可以延后一直到类被首次主动使用的时候进行。关于首次主动使用,会在下文中说明。
[color=red]初始化:[/color]初始化就是为类变量赋正确的值,在连接阶段,为类变量赋了默认的值,在这一步中,会将默认值修改为正确值。所谓正确值,就是程序员在程序中赋予变量的值。初始化过程会在一个类首次主动使用的时候进行。
[b]一个类的初始化过程分为2个步骤:
1 如果该类存在父类,并且父类没有被初始化,则初始化父类。
2 如果该类存在<clinit>方法,执行此方法。[/b]
上面的第一条容易理解,我们来介绍第二条中的<clinit>方法。
[color=red]<clinit>方法叫做类初始化方法,它专门负责执行“类变量初始化语句”和“静态初始化语句”。<clinit>方法在class文件生成时被创建。如果一个类没有类变量初始化语句,也没有静态初始化语句,那么该类就不存在<clinit>方法。[/color]
我们来看下面的例子:
public class Example {
static int width=3;
static int height=1+(int)(Math.random()*3.0);
static int length;
static {
length=10+(int)(Math.random()*3.0);
}
}

当这个类生成class文件时,会在class文件中生成<clinit>方法,<clinit>方法负责初始化类的height变量和length变量。为何没有width变量?weight变量会被编译器特殊处理为常量(这在生成class文件时就已经做好了)。也就是说在程序中的变量在class文件中不一定是变量,对于型如width这种初始化的变量会被优化为常量,存放在常量池中。当其他类引用Example.width时,会在其他类中直接存放一个3,而不是存放一个Example.width的引用。比如在类A中有语句int tmp=Example.width,那么在类A的常量池中会将tmp的值存储为3,而不是Example.width的引用。
[b][color=red]另外,我们需要重点知道的是,jvm会保证<clinit>方法被同步的执行,这就是使用内部静态类实现的单例模式能保证线程安全的原因。[/color][/b]
下面我们来说说[b]什么叫主动使用?[/b]有6种行为被认为是主动使用:
1 创建类的实例
2 调用类中的静态方法
3 操作类中的非常量静态字段
4 调用java API中特定的反射方法
5 初始化一个类的子类
6 指定一个类作为启动初始化类
上面的6种行为其实很好理解,归根到底就是不初始化一个类程序执行不下去的时候,就要初始化一个类。下面给出一个例子:
class Parent{
static int hoursOfSleep=(int)(Math.random()*3.0)+2;
static {
System.out.println("Parent was initialed!");
}
}
class NewBaby extends Parent{
static int hoursOfCry=3+(int)(Math.random()*3.0);
static {
System.out.println("NewBaby was initialed!");
}
}
public class Example {
public static void main(String [] args){
int hours=NewBaby.hoursOfSleep;
System.out.println(hours);
}
static {
System.out.println("Example was initialed!");
}
}


上面的代码输出结果为:
Example was initialed!
Parent was initialed!
2
我们来分析下值命令行中执行java Example之后的过程,首先装载Example的结构到内存中(包括常量池),接着验证,准备,连接。由于Example中没有类变量,不需要为类变量分配内存和赋值操作。接着初始化,初始化的时候会执行static块中的静态初始化语句,输出Example was initialed!。类Example装载完毕之后,就开始执行它的main方法啦!执行到main方法的第一句,需要使用NewBaby的hoursOfSleep,发现NewBaby类的hoursOfSleep变量指向Parent的hoursOfSleep变量。而要使用Parent的hoursOfSleep变量需要先将其初始化(只用初始化才会执行类中的类变量初始化语句和静态初始化语句),因此初始化Parent类,执行Parent类的<clinit>方法,返回hoursOfSleep的值。在执行Parent的<clinit>方法时自然会输出Parent was initialed!。接着程序继续执行主函数的println方法即可。我们可以看到:在程序执行过程中,没有必要初始化NewBaby类。
自此,我们介绍了一个java类装载,连接,初始化的过程,当然,当一个类型不在被使用的时候,我们可以选择将该类型从内存中[color=red]卸载[/color],但是常用的启动类装载器不支持卸载。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值