JVM-类加载

JVM-类加载

编译

JVM默认使用的是解释器+热点代码编译的混合方式。

  1. -Xmixed:混合模式
  2. -Xint:解释模式,启动快,执行慢
  3. -Xcomp:编译模式,启动慢,执行快

JIT对于执行频率高的代码编译成本地代码。

-XX:CompileThreshold = 10000 --检测热点代码

类加载过程

  1. 装载(loading):将class文件放入内存
  2. 链接(linking)
  3. 校验(Verification):校验文件是否符合JVM规范
  4. 准备(Preparation):静态变量赋默认值
  5. 解析(Resolution):将符号引用转为直接引用,包括类、方法、属性等。其实就是将JVM常量池中的引用解析为指针等内存地址直接引用。
  6. 初始化(initializing):调用类初始化代码块儿,给静态变量赋初始值,执行静态代码块儿

类加载器(ClassLoader)

用来加载.class文件。

  1. BootstrapClassLoader:用于加载rt.jar等核心类,是由C++实现的,因此在java中看不到这个类加载器,如果一个类的类加载器为null,那么使用的就是该加载器
  2. ExtClassLoader:用于加载扩展jar包jre/lib/ext/*.jar
  3. AppClassLoader:用于加载classPath指定的内容,即我们自己写的
  4. CustomClassLoader:自定义类加载器

其中3是4的父加载器,2是3的夫加载器,1是2的父加载器。

注意:父加载器不是继承关系,是在源码中final修饰的一个parent变量,是指定好的。

双亲委派

类加载器加载时候,先去委派父加载器加载,父加载器先去缓存查找,如果找到返回,如果没找到再去父类执行此逻辑。直到BootStrapCLassLoad,如果没找到在依次委托子加载器去加载;

双亲委派主要是为了解决安全问题;
假如任何一个加载器都可以加载类,那么可以使用自定义加载器加载自定义一个java.lang.string也可以被加载。如果使用双亲委派,在自定义加载器加载时候,会去父加载器去找,最后加载的是java类库中的class。

自定义类加载器

  1. 继承ClassLoader
  2. 重写findClass方法
  3. 在findClass方法中调用super.defineClass方法
  4. 可以通过调用父类构造方法super(parent)指定父加载器
  5. 可通过重写loadClass方法打破双亲委派,tomcat的热部署就是这个逻辑。

Class类对象

比如String.class

ClassLoader将一个class文件加载至内存中时候:

  1. 将class文件放入到内存中。
  2. 生成一个class类的对象,指向class二进制文件存储的内存地址。

JVM懒加载

JVM在启动时候,不会将所有的class文件加载至内存,只有使用的时候,比如new的时候才会加载对应的class文件

类加载过程中的静态变量和new对象时成员变量的过程

  1. 这两个过程都有赋默认值的过程
  2. 类加载过程中静态变量在链接环节的准备环节,给静态变量赋默认值
  3. new对象时,先申请内存给成员变量赋默认值,调用构造方法时候,给成员变量赋初始值。

硬件层数据一致性问题

在CPU读取数据时候,多个cpu读取同一块儿数据会有数据一致性问题

  1. 通过总线锁或者MESI协议解决数据一直性问题
  2. 缓存行问题,用填充缓存行解决问题

乱序问题

cpu执行速度比cup在内存中读取数据的效率快很多,因此为了提高cpu执行效率,cpu会在读取内存的时候执行其他指令,前提是两条指令没有依赖关系。因此指令重排的问题;

解决乱序问题

  1. cpu层面使用指令建立内存屏障,如X86的sfence、ifence、mfence等。
  2. jvm层级,JVM规范依赖硬件实现,如LoadLoad屏障、StoreStore屏障、SoadStore屏障、StoreLoad屏障

volatile

实现:

  1. java代码层面:volatile标记
  2. class文件层面:ACC_VOLATILE标记
  3. JVM层面:在volatile前后加内存屏障,StoreStoreBarrier/读操作/StoreLoadBarrier LoadLoadBarrier/写操作/LoadsSoreBarrier
  4. 硬件层面:具体的指令实现内存屏障

synchronized

实现:

  1. 字节码层面:ACC_synchronized标记, 指令monitorenter 和 monitorexit指令
  2. JVM层面:C C++ 调用操作系统的同步机制
  3. 硬件层面:X86:lock指令

对象创建过程

  1. class load:装载
  2. class linking: 链接,包括校验、准备、解析
  3. class initializing:初始化
  4. 申请对象内存
  5. 给成员变量默认值
  6. 调用构造方法:成员变量顺序赋初始值,调用构造方法

对象内存存储布局

普通对象:

  1. 对象头: 8字节
  2. class pointer指针:-XX:+UseCompressedClassPointers,开启指针压缩为4字节,默认不开启8字节
  3. 实例数据:引用类型为8字节,-XX:+UseCompressedOops,开启指针压缩为4字节
  4. Padding:补齐,8的倍数

数组对象:

  1. 对象头:8字节
  2. class Pointer指针:同普通对象
  3. 数组长度:4字节
  4. 数组数据
  5. padding:补齐8的倍数

Java如何可以查看对象大小

  1. 使用java Agent,在一个jar包中使用Instrumentation在加载class文件时候截取流文件可以查看对象大小
  2. 在另一个项目里引入jar包,启动时候指定jar文件,-javaagent: jar包全路径
  • 13
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值