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代码块抛出异常,中断finally ...
JVM基础 -- 桥接方法
背景Java语言的重写与JVM的重写并不一致,当在Java语言中为重写而在JVM中为非重写,编译器会通过生成桥接方法来实现Java中的重写语义
桥接方法 – 返回类型Java代码1234567891011121314151617181920@Slf4jpublic class Father { public Number work() { return 1.0; } public static void main(String[] args) { Father father = new Son(); // 实际调用的是桥接方法 Number work = father.work(); log.info("{}", work); }}class Son extends Father { @Override public Double work() { return 2.0; ...
JVM基础 -- 方法调用
重载+重写
重载:方法名相同,但方法描述符不相同的方法之间的关系
重写:方法名相同,并且方法描述符也相同的方法之间的关系
方法描述符
Java:参数类型
JVM:参数类型+返回类型
重载
重载的方法在编译过程即可完成识别
具体到在每个方法调用时,Java编译器会根据传入参数的声明类型(不是实际类型)来选取重载方法
三阶段
在不允许自动装拆箱和可变长参数的情况下,选取重载方法
允许自动装拆箱,但不允许可变长参数的情况下,选取重载方法
在允许自动装拆箱和可变长参数的情况下,选取重载方法
Java编译器在同一阶段找到多个适配的方法,依据形式参数的继承关系,选择最贴切的方法,原则:子类优先
重载来源
同一个类中定义
继承父类非私有同名方法
重写
子类中定义了与父类中非私有的同名实例方法,且参数类型相同
如果是静态方法,那么子类中的方法会隐藏父类中方法
方法重写是Java多态最重要的一种体现形式
静态绑定与+动态绑定
JVM识别重载方法的关键在于类名,方法名和方法描述符
方法描述符:参数类型 + 返回类型
如果在同一个类中出现多个方法名和方法描述符也相同的方法,那么JVM会在类的验证阶段报 ...
JVM基础 -- 类加载
引用类型
类(字节流)
接口(字节流)
数组类(由JVM直接生成)
泛型参数(类型擦除,伪泛型)
类加载过程加载
加载:查找字节流,并且据此创建类的过程
对于数组类,没有对应的字节流,而是由JVM直接生成的
对于其他类而言,JVM需要借助类加载器来完成查找字节流的过程
类加载器
启动类加载器(boot class loader):由C++实现,没有对应的Java对象,在Java中只能用null来指代
除了启动类加载器外,其他的类加载器都是java.lang.ClassLoader的子类,有对应的Java对象
这些类加载器需要先由另一个类加载器(如启动类加载器),加载至Java虚拟机中,方能执行类加载
双亲委派模型
每当一个类加载器接收到加载请求时,会先将请求转发给父类加载器
在父类加载器没有找到所请求的类的情况下,该类加载器才会尝试自己加载
Before Java9
启动类加载器(boot class loader):负责加载最基础、最重要的类(-Xbootclasspath)
扩展类加载器(extension class loader):父类加载器为启动类加载器,负责加载相对次要、但又通 ...
JVM基础 -- 基本类型
boolean类型Java代码1234567public class Foo { public static void main(String[] args) { boolean flag = true; if (flag) System.out.println("Hello, Java!"); if (flag == true) System.out.println("Hello, JVM!"); }}
编译运行12345$ javac Foo.java$ java FooHello, Java!Hello, JVM!
修改字节码运行12# jasm与javap的输出比较类似$ java -cp ./asmtools.jar org.openjdk.asmtools.jdis.Main Foo.class > Foo.jasm.bak
12345678910111213141516171819202122$ tail -n 23 Foo.jasm.bak | ...
JVM基础 -- 运行过程+运行效率
虚拟机视角
将class文件加载到JVM中,加载后的Java类会被存放在方法区,实际运行时,虚拟机会执行方法区内的代码
JVM同样会将内存划分出堆和栈来存储运行时数据,栈会细分本地方法栈和Java方法栈
PC寄存器:用于记录各个线程的执行位置
在运行过程中,每当调用进入一个Java方法,JVM会在当前线程的Java方法栈中生成一个栈帧
栈帧用于存放局部变量表和操作数
栈帧的大小是提前计算好的,并且JVM不要求栈帧在内存空间里连续分布
当退出当前执行的方法时,不管是正常返回还是异常返回,JVM都会弹出并舍弃当前线程的当前栈帧
硬件视角
Java字节码无法直接执行,需要JVM将字节码翻译成机器码,有两种形式:解析执行+即时编译
解释执行:逐条将字节码翻译成机器码并执行,无需等待编译
即时编译(JIT):将一个方法中包含的所有字节码编译成机器码后再执行,实际运行速度更快
HotSpot默认采用混合模式,先解析执行 字节码,然后将其反复执行的热点代码,以方法为单位 进行即时编译
即时编译建立在2-8定律的假设之上
对于占据大部分的不常用代码,无需耗费时间将其编译成机器码,而是采用解释执行的方式
...
Kafka -- 消费者
基本概念消费者 + 消费者群组
消费者从属于消费者群组
一个消费者群组里的消费者订阅的是同一个主题,每个消费者接收主题的部分分区的消息
消费者横向扩展1个消费者
主题T1有4个分区,然后创建消费者C1,C1是消费者群组G1里唯一的消费者,C1订阅T1
消费者C1将接收主题T1的全部4个分区的消息
2个消费者
如果群组G1新增一个消费者C2,那么每个消费者将分别从两个分区接收消息
假设C1接收分区0和分区2的消息,C2接收分区1和分区3的消息
4个消费者
如果群组G1有4个消费者,那么每个消费者可以分配到一个分区
5个消费者
如果群组G1有5个消费者,_**消费者数量超过主题的分区数量**_,那么有1个消费者就会被**闲置**,不会接收到任何消息
总结
往群组里增加消费者是横向伸缩消费能力的主要方式
消费者经常会做一些高延迟的操作,比如把数据写到数据库或HDFS,或者使用数据进行比较耗时的计算
有必要为主题创建大量的分区,在负载增长时可以加入更多的消费者,减少消息堆积
不要让消费者的数量超过主题分区的数量,多余的消费者只会被闲置
消费者群组横向扩展
Kafka设计的主要目标之一, ...
Kafka -- Avro + Twitter Bijection
Avro + Kafka Native API
比较繁琐
编译Schema
依赖于Avro实现自定义的序列化器和反序列化器
引入依赖12345<dependency> <groupId>com.twitter</groupId> <artifactId>bijection-avro_2.12</artifactId> <version>0.9.6</version></dependency>
Schema路径:src/main/resources/user.json
123456789{ "type": "record", "name": "User", "fields": [ {"name": "id", "type": "int"}, ...
Kafka -- Avro + Kafka Native API
Schema123456789101112131415{ "namespace": "me.zhongmingmao.avro", "type": "record", "name": "Stock", "fields": [ {"name": "stockCode", "type": "string"}, {"name": "stockName", "type": "string"}, {"name": "tradeTime", "type": "long"}, {" ...
Kafka -- Avro入门
引入依赖12345<dependency> <groupId>org.apache.avro</groupId> <artifactId>avro</artifactId> <version>1.8.2</version></dependency>
1234567891011121314151617<plugin> <groupId>org.apache.avro</groupId> <artifactId>avro-maven-plugin</artifactId> <version>1.8.2</version> <executions> <execution> <phase>generate-sources</phase> <goals> ...