类型的生命周期包括:装载,连接,初始化,卸载(和类装载器有关,不一定有)。
装载:将类的结构信息装载到内存中(这其中就包括很重要的角色--类的常量池,但是符号引用还没有解析),装载完毕之后会生成一个该类的Class对象。
连接:连接又包括
验证,准备和解析。验证就是检验class文件语法等正确性。准备是为类变量分配内存并为他赋默认值。解析是将在装载阶段装载到内存中的常量池的符号引用解析为直接引用,这一步可以延后一直到类被首次主动使用的时候进行。关于首次主动使用,会在下文中说明。
初始化:初始化就是为类变量赋正确的值,在连接阶段,为类变量赋了默认的值,在这一步中,会将默认值修改为正确值。所谓正确值,就是程序员在程序中赋予变量的值。初始化过程会在一个类首次主动使用的时候进行。
一个类的初始化过程分为2个步骤:
1 如果该类存在父类,并且父类没有被初始化,则初始化父类。
2 如果该类存在<clinit>方法,执行此方法。
上面的第一条容易理解,我们来介绍第二条中的<clinit>方法。
<clinit>方法叫做类初始化方法,它专门负责执行“类变量初始化语句”和“静态初始化语句”。<clinit>方法在class文件生成时被创建。如果一个类没有类变量初始化语句,也没有静态初始化语句,那么该类就不存在<clinit>方法。
我们来看下面的例子:
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的引用。
另外,我们需要重点知道的是,jvm会保证<clinit>方法被同步的执行,这就是使用内部静态类实现的单例模式能保证线程安全的原因。
下面我们来说说
什么叫主动使用?有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类装载,连接,初始化的过程,当然,当一个类型不在被使用的时候,我们可以选择将该类型从内存中
卸载,但是常用的启动类装载器不支持卸载。
分享到:
相关推荐
12. **代码优化**:减少不必要的对象创建,及时释放不再使用的对象引用,避免大量短生命周期对象进入老年代,都可以减少Full GC的压力。 13. **类加载机制**:理解类加载的双亲委派模型,避免类的不必要加载,可以...
- 通过调整年轻代和老年代的比例(`-XX:NewRatio`),可以优化对象的生命周期管理。 - 使用并行垃圾回收策略(`-XX:+UseParallelGC`)提高垃圾回收效率。 5. **性能监控与分析**: - 利用JMX监控工具来监控JVM的...
使用Pact JVM Model 2.10进行契约测试,确保服务间的通信无误,同时通过Error Prone Gradle Plugin在编码阶段就捕获并修正可能的错误,这大大降低了缺陷的生命周期,提高了开发效率和软件的可靠性。 在实际应用中,...
新生代主要用于存储新创建的对象,老年代则存放生命周期较长的对象,而持久代主要存储类的元数据。 二、GC(垃圾收集) GC是JVM自动进行内存管理的过程,其目标是回收不再使用的对象,以释放内存空间。GC分为Minor...
- **新生代与老年代**:根据对象生命周期划分内存区域,新创建的对象通常在新生代,生存时间较长的则进入老年代。 3. **内存溢出问题** - **堆内存溢出**:过多的对象实例导致堆空间不足。 - **栈内存溢出**:...
栈是线程私有的,每个线程在启动时都会创建一个虚拟机栈,栈的生命周期与线程同步,即随线程创建而创建,随线程结束而销毁。栈的主要任务是为每个方法的执行提供一个栈帧(Stack Frame),栈帧存储了方法执行过程中...
例如,通过调整新生代和老年代的比例,可以优化对象的生命周期管理;通过选择合适的垃圾收集器,可以平衡系统的响应时间和吞吐量。 总结来说,"jvm-services"是关于Java及JVM生态的深度探索,涵盖了JVM的架构、服务...
其中,分代收集根据对象的生命周期将堆划分为新生代和老年代,分别采用不同的收集策略。 在"jvm-int-gc-benchmark"项目中,我们可以通过基准测试来评估和比较不同垃圾收集器的性能。基准测试是衡量系统性能的重要...
- 长生命周期的对象持有短生命周期对象,导致短生命周期对象无法被回收,是常见的内存泄漏类型。 - 使用内存分析工具如MAT(Memory Analyzer Tool)可以帮助定位内存泄漏问题。 以上内容仅是JVM调优的一部分,...
本文将详细介绍Java虚拟机(JVM)的内部机理和实现原理,从类型的生命周期、方法区、常量池、类加载器、垃圾收集器、栈和局部变量等方面对JVM进行深入解析。 类型的生命周期 类型的生命周期是JVM中最重要的部分,...
JVM 在执行 Java 程序的过程中,将其所管理的内存划分为几个不同的区域,每个区域有其特定的功能和生命周期。理解这些区域如何工作对于深入理解 JVM 至关重要。 ##### 1.1 方法区 (Method Area) - **定义**:方法...
分代收集算法是现代JVM采用的主流算法,它基于这样的观察:大多数对象的生命周期都比较短,而存活时间较长的对象比较少。 在垃圾回收过程中,处理内存碎片是一个重要的考量点。内存碎片是指分配给对象的内存空间不...
2. 分代内存模型:JVM将内存分为新生代、老年代和持久代,根据对象生命周期的不同,进行不同策略的垃圾收集,提高了内存利用率和程序性能。 三、类加载机制 1. 类加载器:Java 11中,JVM的类加载器系统确保了类的...
解决方法包括增大堆内存、检查并优化对象生命周期,或者采用更有效的数据结构和算法。而PermGen/Metaspace(Java 8之后的元空间)溢出则可能是因为类加载过多,尤其是动态加载大量类时。解决方案可能涉及调整-XX:...
- 类的生命周期包括加载、验证、准备、初始化和卸载五个阶段。 - 双亲委派模型:类加载器在加载类时,会将任务委托给父类加载器,直到Bootstrap ClassLoader。 3. **内存区域**: - 程序计数器:记录当前线程...
老年代的对象生命周期更长,回收频率较低。 3. **永久代**:用于存储类元数据、静态变量等信息。从Java 8开始,永久代被元空间(Metaspace)取代。 #### 总结 综上所述,JVM性能调优涉及多个方面,包括理解参数传递...
* 分代收集算法:基于对象生命周期的垃圾回收算法,把对象分为年青代、年老代、持久代,对不同生命周期的对象使用不同的算法进行回收。 JVM 中的垃圾回收器 JVM 中的垃圾回收器可以分为三种:串行收集、并行收集...
理解对象生命周期、引用类型和垃圾收集器的工作原理对于优化程序性能至关重要。 6. **JVM优化**:包括堆内存调整、栈内存优化、方法区参数配置、编译策略选择等。例如,通过-Xms和-Xmx设置堆大小,-XX:MaxPermSize...