免费表格模板网站,网站建设三秒原则,网页是网站吗,部队网站建设报告目录
类和对象的加载过程
类的生命周期
类的加载过程
加载
验证
准备
解析
初始化
类卸载
对象的加载过程 类和对象的加载过程
什么是类加载和对象加载? 类加载#xff08;Class Loading#xff09;#xff1a;这是指JVM在运行时将类的字节码文件加载到内存中的…目录
类和对象的加载过程
类的生命周期
类的加载过程
加载
验证
准备
解析
初始化
类卸载
对象的加载过程 类和对象的加载过程
什么是类加载和对象加载? 类加载Class Loading这是指JVM在运行时将类的字节码文件加载到内存中的过程。类加载的主要任务是找到类的字节码文件然后读取它并存储在方法区中。这个过程是由类加载器完成的。 对象加载Object Loading这是对象在堆内存中分配空间的过程。当我们在代码中创建一个类的实例时JVM会在堆内存中为这个对象分配空间并将其实例化。这个过程是在类加载之后发生的。
简单来说类加载是将类的字节码文件加载到内存中Jvm虚拟机的方法区中而对象加载是为这个类的实例在堆内存中分配空间并初始化。 类的生命周期
类从被加载到虚拟机内存中开始到卸载出内存为止它的整个生命周期可以简单概括为 7 个阶段加载Loading、验证Verification、准备Preparation、解析Resolution、初始化Initialization、使用Using和卸载Unloading。其中验证、准备和解析这三个阶段可以统称为连接Linking。
这 7 个阶段的顺序如下图所示 类的加载过程
Class 文件需要加载到虚拟机中之后才能运行和使用那么虚拟机是如何加载这些 Class 文件呢
系统加载 Class 类型的文件主要三步加载-连接-初始化。连接过程又可分为三步验证-准备-解析。
加载
主要目的就是使用二进制字节流将class文件加载进Jvm的方法区
类加载过程的第一步主要完成下面 3 件事情 通过全类名获取定义此类的二进制字节流。 将字节流所代表的静态存储结构转换为方法区的运行时数据结构。 在内存中生成一个代表该类的 Class 对象作为方法区这些数据的访问入口。
加载这一步主要是通过我们后面要讲到的 类加载器 完成的。类加载器有很多种当我们想要加载一个类的时候具体是哪个类加载器加载由 双亲委派模型 决定不过我们也能打破由双亲委派模型。 验证
验证是连接阶段的第一步这一阶段的目的是确保 Class 文件的字节流中包含的信息符合《Java 虚拟机规范》的全部约束要求保证这些信息被当作代码运行后不会危害虚拟机自身的安全。
验证的阶段因为比较关键保证了后面的运行安全。这个阶段是比较耗费资源的但是这个验证也是可以自己手动进行关闭的。如果我们的代码都是自己编写并且引用的代码依赖都是自己使用过多次的在生产环境的实施阶段就可以考虑使用 -Xverify:none 参数来关闭大部分的类验证措施以缩短虚拟机类加载的时间。
验证阶段主要由四个检验阶段组成 文件格式验证Class 文件格式检查基于二进制字节流进行实现确保数据能够安全的加载到方法区之内。 元数据验证字节码语义检查 字节码验证程序语义检查 符号引用验证类的正确性检查
准备
准备阶段是正式为类变量静态变量分配内存并设置类变量初始值的阶段这些内存都将在方法区中分配。对于该阶段有以下几点需要注意 这时候进行初始化的都只是类变量而不是实例变量。实例变量在对象进行初始化的时候都会被分配在堆中 HotSpot 已经把原本放在永久代的字符串常量池、静态变量等移动到堆中这个时候类变量则会随着 Class 对象一起存放在 Java 堆中。 这个阶段设置的初始值只是每个类型的默认值比如 static int a 11;这个时候初始化了的a的值为0而不是11。但是如果是final关键字修饰的变量则直接会被赋值为设置的值。
解析
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。 解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用限定符 7 类符号引用进行。 虚拟机在类加载的解析阶段会遍历类的常量池查找所有的符号引用。 对于每一个符号引用虚拟机会尝试找到它引用的目标类、方法或字段。这可能需要加载其他的类或接口。 如果找到了目标虚拟机就会将这个符号引用替换为一个直接引用这个直接引用可以直接访问目标类、方法或字段。 如果找不到目标或者目标不是一个有效的类、方法或字段例如尝试访问一个不存在的类或方法那么解析过程就会失败并且会抛出一个异常。
初始化
初始化阶段是执行初始化方法 clinit ()方法的过程是类加载的最后一步这一步 JVM 才开始真正执行类中定义的 Java 程序代码(字节码)。 说明clinit ()方法是编译之后自动生成的。 对于初始化阶段虚拟机严格规范了有且只有 6 种情况下必须对类进行初始化(只有主动去使用类才会初始化类) 当遇到 new、 getstatic、putstatic 或 invokestatic 这 4 条字节码指令时比如 new 一个类读取一个静态字段(未被 final 修饰)、或调用一个类的静态方法时。 当 jvm 执行 new 指令时会初始化类。即当程序创建一个类的实例对象。 当 jvm 执行 getstatic 指令时会初始化类。即程序访问类的静态变量(不是静态常量常量会被加载到运行时常量池)。 当 jvm 执行 putstatic 指令时会初始化类。即程序给类的静态变量赋值。 当 jvm 执行 invokestatic 指令时会初始化类。即程序调用类的静态方法。 使用 java.lang.reflect 包的方法对类进行反射调用时如 Class.forname(...), newInstance() 等等。如果类没初始化需要触发其初始化。 初始化一个类如果其父类还未初始化则先触发该父类的初始化。 当虚拟机启动时用户需要定义一个要执行的主类 (包含 main 方法的那个类)虚拟机会先初始化这个类。 MethodHandle 和 VarHandle 可以看作是轻量级的反射调用机制而要想使用这 2 个调用 就必须先使用 findStaticVarHandle 来初始化要调用的类。 类卸载
卸载类即该类的 Class 对象被 GC。
卸载类需要满足 3 个要求: 该类的所有的实例对象都已被 GC也就是说堆不存在该类的实例对象。 该类没有在其他任何地方被引用 该类的类加载器的实例已被 GC
所以在 JVM 生命周期内由 jvm 自带的类加载器加载的类是不会被卸载的。但是由我们自定义的类加载器加载的类是可能被卸载的。
只要想通一点就好了JDK 自带的 BootstrapClassLoader, ExtClassLoader, AppClassLoader 负责加载 JDK 提供的类所以它们(类加载器的实例)肯定不会被回收。而我们自定义的类加载器的实例是可以被回收的所以使用我们自定义加载器加载的类是可以被卸载掉的。 对象的加载过程 Java 对象的创建过程——五步 检查类是否已经被加载 new关键字时创建对象时首先会去运行时常量池中查找该引用所指向的类有没有被虚拟机加载如果没有被加载那么会进行类的加载过程。加载将class文件读取到内存中使用加载器进行加载 为对象分配内存空间 当类加载检查通过后虚拟机会为新生对象分配内存空间对象所需内存空间的大小在类加载完成后就已经确定了。为新生对象分配内存空间其实就是在Java堆中划分出一块确定大小的内存分配给新生对象。分配内存的方式有“指针碰撞”和“空闲列表”两种选择哪种分配方式取决于Java堆内存是否规整。 内存分配的两种方式 指针碰撞 使用场合堆内存规整即没有内存碎片的情况下。 实现原理将用过的内存都整合到一边没有用过的内存放到另一边中间有一个分界指针当需要为新对象分配内存空间时只需要将分界指针向没有用过的内存一侧移动对象内存大小位置即可。 空闲列表 使用场合堆内存不规整的情况下。JDK8默认的GR垃圾回收方式是不规整的方式 实现原理虚拟机会维护一个列表该列表记录了哪些内存是可用的当需要为新对象分配内存空间时只需要在列表中找一块足够大小的内存分配给对象实例然后更新列表记录。 选择以上两种方式中的哪一种取决于 Java 堆内存是否规整。而 Java 堆内存是否规整取决于 GC 收集器垃圾采用的垃圾收集算法 对象初始化零值 内存分配完成后虚拟机需要将新分配的内存空间都初始化为零值(不包括对象头)这一步操作保证了对象的实例字段可以在Java代码中可以不赋初始值就直接使用程序能够访问这些实例字段的数据类型所对应的零值。 设置对象头 初始化零值之后虚拟机需要对对象头进行必要的设置例如这个对象是哪个类的实例如何才能找到这个类的元数据信息对象的哈希码、GC 分代年龄、锁标志位、偏向锁标志位、线程持有的锁信息、对象是否可用等信息会存放到对象头中。另外根据虚拟机当前运行状态的不同如是否启用偏向锁等对象头会有不同的设置方式。 执行init方法 执行完new指令后会接着执行init方法将对象按照程序员的需求来进行初始化这样一个真正可用的对象才算完全产生出来。
上述为无父类的对象创建过程。对于有父类的对象创建过程还需满足如下条件 先加载父类再加载本类 先执行父类的实例的初始化方法init成员变量、构造代码块父类的构造方法执行本类的实例的初始化方法init成员变量、构造代码块本类的构造方法。