目录
1、jvm类加载机制
类加载器就是根据指定全限定名称将 class 文件加载到 JVM 内存,然后再转化为 class 对象。
四种类加载器:
启动类加载器(Bootstrap ClassLoader):用来加载jre下lib下java核心类库,无法被java程序直接引用。
扩展类加载器(extensions class loader):它用来加载 Java 的扩展库jre下lib下ext目录下的包。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。
用户自定义类加载器,通过继承 java.lang.ClassLoader类的方式实现。重写findClass方法
加载:将字节码文件加载到内存
连接
验证:校验字节码文件的准确性
准备:给类的静态变量分配内存,并赋予初始值
解析:将静态方法符号引用转化为直接引用(内存地址)
初始化:将静态变量赋予指定的值,执行静态代码块
双亲委派机制:先判断系统类加载器r是否已经加载过》判断extclassLoader是否已经加载过》
判断启动类加载器是否已经加载过,没有加载过就尝试加载》extclassLoader尝试加载》
appclassLoader加载
源码:
类加载器收到了类加载的请求,它首先不会自己去加载这个类,而是把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,这样所有的加载请求都会被传送到顶层的启动类加载器中,只有当父加载无法完成加载请求时,子加载器才会尝试去加载类。
为什么设计双亲委派:
》沙箱安全机制:防止篡改核心api
》避免类重复加载
自定义类加载器,打破双亲委派机制:继承 java.lang.ClassLoader,重写loadclass方法,不委托父加载器,但是注意核心类和扩展类还是别用自定义的类加载器。
2、jvm内存模型
3、JVM对象创建
1、类加载检查
执行new指令(new关键、对象克隆、对象序列化等)时,先检查这个指令的参数能否在常量池中定位到类的符号引用,并且检查这个类是否已被加载。如果没有,必须先执行类加载过程。
2、分配内存
对象所需内存大小在类加载完后便可确定,将一块确定大小的内存从堆中划分出来。
划分内存的方法:
指针碰撞(默认):如果堆中内存绝对规整,所有用过的内存放在一边,空闲的在另一边,中间放着一个指针作为分界点,那分配内存就是把指针向空闲一边挪动一段和对象大小相等的距离。
空闲列表:如果堆中内存不规整,虚拟机必须维护一个列表记录哪些内存空闲可用,找一块足够大的分配给对象并记录。
分配时并发问题解决:CAS、TLAB(1.8默认,每个线程在堆中预先分配一小块内存)。
3、初始化
虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头)。
4、设置对象头
初始化后,需要对对象进行必要的设置。对象在内存中存储的布局可以分为3块:对象头、实例数据和对齐填充。
对象头包括两部分信息:一部分是存储对象自身的运行时数据,如hashCode、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏西时间戳等。另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针确定这个对象是哪个类的实例。
32位:对象头:mark word(4字节)+类型指针(4字节)=8字节,数组对象对象头:mark word(4字节)+类型指针(4字节)+长度(4字节)=12字节。
64:对象头:第一行和第二行4+4=8(8*8=64)是mark word,第三行类型指针指针压缩(节省内存)占4字节。
对象:最后一行4字节为了对齐填充,保证对象所占内存是8字节整数倍。
对象头:第一行和第二行4+4=8(8*8=64)是mark word,第三行类型指针占4字节
5、执行<init>
为属性赋值和执行构造方法。
4、内存分配机制