目录
类生命周期
类的生命周期:class文件--》java虚拟机内存--》卸载
(加,验,准,解,初,使,卸)
加载,查找并加载类的二进制数据class文件,方法区(类的类信息),堆(class文件对应的类实例)
验证,确保加载的类信息是正确的
准备,为类的静态变量进行初始化,分配空间并赋予初始值
解析,将符号引用转化为直接引用(符号转换为地址)
初始化,jvm对类进行初始化,对静态变量赋予正确值(静态代码块)
使用
卸载
类加载器
类加载器:(自定义类加载器,系统类加载器,扩展类加载器,引导类加载器)“自,系,扩 ,引”
BootStrapClassLoader(c)
|- 加载(JDK/JRE/LIB 目录下的 java.* 开头的类)
ExtClassLoader
|- 加载(JDK/JRE/LIB 目录下的 javax.* 开头的类)
AppClassLoader
|- 系统类加载器,用户自定义的任何类加载器都将该类加载器作为自定义类加载器的父加载器.
用户自定义加载器
|- 加载一般是二进制数据class文件,可以自定义,流、网络、数据库
双亲委派模型
请注意双亲委派模式中的父子关系并非通常所说的类继承关系,而是采用组合关系来复用父类加载器的相关代码
如果一个类加载器收到了类的加载请求,他首先不会自己去加载,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中
只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该类加载,子加载器才会尝试自己去加载该类
目的:
1. Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次
2. 防止核心API库被随意篡改,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class
class对象是否为同一个类对象
同时满足:
1.类的完整路径名称必须是一样的,路径名+包名
2.加载类的ClassLoader必须是相同的,因为不同的ClassLoader类加载器拥有不同的相互独立的类名称空间,所以加载的class对象也会存在不同的类名空间中
双亲委派模型中,创建类的Class对象的时候,首先会通过 Class<?> c = findLoadedClass(name); 去缓存中查找,如果类的完整路径名称一样,则不会再次加载。如果要重新加载可以通过 findClass() 方法,避免去缓存中查找数据。
类的加载方式
显示加载
指的是在代码中通过调用ClassLoader加载class对象,如直接使用
Class.forName(name)
或this.getClass().getClassLoader().loadClass()
加载class对象。隐式加载
指的是虚拟机在加载某个类的class文件时,该类的class文件中引用了另外一个类的对象,此时额外引用的类将通过JVM自动加载到内存中。
类属性加载顺序
-
static静态代码块和静态成员
-
普通成员
-
构造函数执行
当具有多个静态成员和静态代码块或者多个普通成员时,初始化顺序和成员在程序中申明的顺序一致。
-
程序启动需要触发main方法的时候,虚拟机会先触发这个类的初始化
-
使用new关键字实例化对象、读取或设置一个类的静态字段(被final修饰、JIT时放入常量池的静态字段除外)、调用一个类的静态方法,会触发初始化
-
当初始化一个类的时候,如果其父类没有初始化,则需要先触发其父类的初始化
所有的初始化过程中clinit()方法,保证了静态变量和静态语句块总是最先初始化的,并且一定是先执行父类clinit(),再执行子类的clinit()。
对象大小计算
使用Lucene进行对象大小的计算
pom文件,使用最新版本的8.8.1 没有 humanSizeOf 的方法
<!--加载内存查看工具-->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>4.0.0</version>
</dependency>
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.lucene.util.RamUsageEstimator;
import org.junit.Test;
/**
* @author yangLongFei 2021-03-08-15:56
*/
public class TestObject {
@Data
@NoArgsConstructor
@AllArgsConstructor
class Person {
private int age;
private double height;
}
@Test
public void test1() {
/**
* byte
* boolean
*
* short
* char
*
* int
* float
*
* long
* double
*/
Person[] people = new Person[]{new Person(10, 103), new Person(8, 113), new Person(12, 135),};
//对象占用的内存空间 144 bytes
System.out.println(RamUsageEstimator.humanSizeOf(people));
/**
* int 4
* double 8
*
* 对象头 8
* 指针 4/8
* 对齐填充 8
*
* 8+4+8+4 = 24
*
* 48
*/
Person person = new Person(1, 18);
//对象占用的内存空间 48 bytes
System.out.println(RamUsageEstimator.humanSizeOf(person));
System.out.println();
}
}