JVM进阶 -- 浅谈逃逸分析
概念 在JVM即时编译语境下,逃逸分析将判断新建的对象是否逃逸 即时编译器判断对象是否逃逸的依据 对象是否被存入堆中(静态字段或者堆中对象的实例字段) 堆是线程共享的,其他线程可以获得该对象的引用 对象是否被传入未知代码 JVM即时编译是以方法为单位的 对于方法中未被内联的方法调用,即时编译器会将其当做未知代码 方法调用的调用者以及参数是逃逸的 注:方法内联可以简单理解,在即时编译过程中遇到方法调用时 将目标方法的方法体纳入到编译范围之中,并取代原方法调用的优化手段 foreach语法糖 12345public void forEach(ArrayList<Object> list, Consumer<Object> f) { for (Object obj : list) { f.accept(obj); }} 等价代码 1234567public void forEach(ArrayList<Object> list, Consumer<Object> f) ...
JVM基础 -- 字节码
操作数栈 JVM是基于栈的计算模型 在解析过程中,每当为Java方法分配栈帧时 执行每条执行之前,JVM要求该指令的操作数已被压入操作数栈中 在执行指令时,JVM会将该指令所需要的操作数弹出,并将该指令的结果重新压入栈中 iadd 执行iadd之前,栈顶的元素为int值1和int值2 执行iadd指令会将弹出这两个int,并将求得的和int值3压入栈中 iadd只消耗栈顶的两个元素,iadd并不关心更远的元素,也不会对它们进行修改 dup + pop dup和pop只能处理非long和非double类型的值 long类型和double类型需要占据两个栈单元,对应使用dup2和pop2 dup dup:复制栈顶元素 dup指令常用于复制new指令生成的未经初始化的引用 123456789101112131415public void dup() { Object o = new Object();}// 对应的字节码public void dup(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2...
JVM进阶 -- 浅谈即时编译
概念 即时编译是用来提升应用运行效率的技术 代码会先在JVM上解释执行,之后反复执行的热点代码会被即时翻译成为机器码,直接运行在底层硬件上 分层编译 HotSpot包含多个即时编译器:C1、C2和Graal(Java 10,实验性) 在Java 7之前,需要根据程序的特性选择对应的即时编译器 对于执行时间较短或对启动性能有要求的程序,采用编译效率较快的C1,对应参数:-client 对于执行时间较长或对峰值性能有要求的程序,采用生成代码执行效率较快的C2,对应参数:-server Java 7引入了分层编译(-XX:+TieredCompilation),综合了C1的启动性能优势和C2的峰值性能优势 分层编译将JVM的执行状态分了5个层次 0:解释执行(也会profiling) 1:执行不带profiling的C1代码 2:执行仅带方法调用次数和循环回边执行次数profiling的C1代码 3:执行带所有profiling的C1代码 4:执行C2代码 通常情况下,C2代码的执行效率比C1代码高出30%以上 对于C1代码的三种状态,按执行效率从高至低:1层 > 2层 > 3层 1...
JVM基础 -- Java语法糖
自动装拆箱Java代码12345public int foo() { List<Integer> list = new ArrayList<>(); list.add(0); return list.get(0);} 字节码12345678910111213141516171819202122232425public int foo(); descriptor: ()I flags: ACC_PUBLIC Code: stack=2, locals=2, args_size=1 0: new // class java/util/ArrayList 3: dup 4: invokespecial // Method java/util/ArrayList."<init>":()V 7: astore_1 8: aload_1 9: iconst_0 // 自动装箱 ...
JVM基础 -- 浅谈synchronized
抽象算法synchronized代码块12345public void foo(Object lock) { synchronized (lock) { lock.hashCode(); }} 12345678910111213141516171819202122232425public void foo(java.lang.Object); descriptor: (Ljava/lang/Object;)V flags: ACC_PUBLIC Code: stack=2, locals=4, args_size=2 0: aload_1 1: dup 2: astore_2 3: monitorenter 4: aload_1 5: invokevirtual #2 // Method java/lang/Object.hashCode:()I 8: pop 9: aload_2 ...
JVM基础 -- Java内存模型
JIT的重排序Java代码1234567891011121314public class JMM { private int a = 0; private int b = 0; public void method1() { int r2 = a; // A1 b = 1; // A2 } public void method2() { int r1 = b; // B1 a = 2; // B2 }} 单线程,method1->method2,**(r1,r2)=(1,0)** 单线程,method2->method1,**(r1,r2)=(0,2)** 多线程,没有重排序,A1->B1->A2->B2,**(r1,r2)=(0,0)** 多线程,重排序,A2->B1->B2->A1,**(r1,r2)=(1,2)** As-If-Serial 在单线程情...
JVM基础 -- 垃圾回收基础
判定对象存亡垃圾回收标记的是非垃圾 引用计数法 为每个对象添加一个引用计数器,用来统计指向该对象的引用个数 如果有一个引用,被赋值为某一对象,那么将该对象的引用计数器+1 如果指向某一对象的引用,被赋值为其他值,那么该对象的引用计数器-1 一旦某个对象的引用计数器为0,说明对象已经死亡 缺点 额外的空间来存储计数器 + 繁琐的更新操作 无法处理循环引用的场景,造成内存泄露 可达性分析 将一系列GC Roots作为初识存活对象合集 标记:从该集合出发,探索所有能够被该集合引用到的对象,并将其加入到该集合中 最终未被探索到的对象便是死亡,可以被回收 GC Roots:堆外指向堆内的引用,一般包括 Java方法栈帧中的局部变量 已加载类的静态变量 已启动且未停止的Java线程 JNI MethodHandles STW + 安全点 JVM中的STW是通过安全点机制来实现的 当JVM收到STW请求时,会等待所有的线程都到达安全点,才允许请求STW的线程进行独占地工作 安全点的初衷并不是让其他线程停下,而是找到一个稳定的执行状态 在这个执行状态下,JVM的堆栈不会发生变化 垃圾回收器能够安...
JVM基础 -- Java对象的内存布局
创建对象 new + 反射 通过调用构造器来初始化实例字段 Object.clone + 反序列化 通过直接复制已有的数据,来初始化新建对象的实例字段 Unsafe.allocateInstance 不会初始化实例字段 123456// Foo foo = new Foo();对应的字节码// new指令:请求内存0: new // class me/zhongmingmao/basic/jol/Foo3: dup// invokespecial指令:调用构造器4: invokespecial // Method "<init>":()V Java构造器默认构造器如果一个类没有定义任何构造器,那么Java编译器会自动添加一个无参数的构造器 Java代码123456// 未定义任何构造器public class Foo { public static void main(String[] args) { Foo foo = new Foo(); }} 字节...
JVM基础 -- 浅谈反射
反射API获取Class对象 Class.forName() object.getClass() 类名.class Integer.TYPE指向int.class 数组类型:类名[].class 1public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int"); 常规用法 newInstance() 生成该类实例 需要无参构造器 isInstance(Object) 判断一个对象是否为该类的实例 语法上等同于instanceOf,在JIT优化时会有所差别 Array.newInstance(Class<?>, int) 构造该类型的数组 getFields()/getConstructors()/ getMethods() 访问类成员 带Declared的方法不会返回父类成员,但会返回私有成员;不带Declared的方法恰好相反 获取类成员后 Field/Construct...
JVM基础 -- 异常处理
抛出异常 + 捕获异常抛出异常 显式抛异常的主体是应用程序,使用throw关键字 隐式抛异常的主体是JVM,在JVM执行过程中,碰到无法继续执行的异常状态时,自动抛出异常 例如ArrayIndexOutOfBoundsException 捕获异常 try代码块 标记需要异常监控的代码 catch代码块 定义了针对指定类型的异常处理器 多个catch代码块,JVM会从上至下匹配异常处理器 前面catch代码块所捕获的异常类型不能覆盖后边的,否则编译器会报错 finally代码块 声明一段必定运行的代码 程序正常执行,未抛出异常,try -> finally 程序抛出异常未被捕获,try(throw A) -> finally -> throw A 程序抛出异常并被捕获,try(throw A) -> catch(A) -> finally 程序抛出异常并被捕获,并且catch代码块也抛出异常,try(throw A) -> catch(A, throw B) -> finally -> throw B finally代码块抛出异常,中断fina...















