Linux IO 模型
1.阻塞IO
2.异步非阻塞IO
3.IO复用
4.信号驱动IO
5.异步IO
Java 类加载器
-
自定义类加载器 Custom ClassLoader
-
启动类加载器 Application ClassLoader
-
扩展类加载器 Extension ClassLoader
-
引导类加载器 Bootstrap ClassLoader
JVM运行时数据区
线程私有:
1.程序计数器
记录程序运行位置
2.线程私有栈
方法执行的栈
3.本地方法栈
通过JNI调用c,c#语言的方法
线程共享:
1.堆
空间布局
新生代 1/3
伊甸园 8/10
from区 1/10
to区 1/10
老年代 2/3
对象什么时候进入老年代
1.15次gc之后
对象对象头的分代是4bit,区间是0-15
2.大对象直接进入
大于Eden50%,运行时动态调整
3.空间担保
gc之后,剩下对象还是大于from区
4.动态
大于Eden和from区
2.方法区
具体实现
1.8之前:永久代,在堆内存中
1.8之后:元空间,在OS的内存中
执行引擎
1.解释器
1.1字节码解释器:
解释执行
Java字节码 -> c++ -> 硬编码
1.2模板解释器:编译执行
Java字节码 -> 硬编码
***jvm三种解释器模式
纯字节码解释器: -Xint
纯模板解释器 —Xcomp
字节码解释器 + 模板解释器 -Xmixed
效率:程序越大 -Xmixed > -Xcomp > -Xint
2.即时编译器(编译给模板解释器使用的)
C1编译器:client模式下的即时编译器
1.触发条件比C2比较宽松:需要收集的数据比较少
2.编译优化的比较浅:基本运算子啊编译的时候去掉了
3.c1编译器编译生成的代码执行效率比C2低
C2编译器:server模式下的即时编译器
1.触发条件比较严格,一半来说,程序运行一段时间以后触发
2.优化比较深
3.编译生成的代码执行效率较C1更高
触发条件:
硬编码
热点代码
最小单位不是一个函数,而是代码块(for,while)
程序初期触发C1即时编译器
程序运行一段时间之后触发C2编译器
clien模式下
N的默认值:1500
server模式下
N的默认值:100000
硬编码
热点代码块
保存在方法区:CodeCash
server编译器模式下-代码缓存大小起始于160K
client编译器模式下-代码缓存大小起始于2496K
热度衰减
LRU算法
阿里热冷机切换事故(热机:指代码运行一段时间的机器;冷机:新机器)
因为但是代码触发及时编译优化,流量瞬间打过去,冷机器就直接挂了,优化之后的代码,执行效率更高,所以能抗住更多的并发
即时编译是怎么运行的
通过VM_THREAD线程(守护线程)例如:
GC
即时编译器
将这个及时编译任务写入队列queue
vm_thread从这个队列中读取任务,并运行
即时编译器有多少线程,如何调优
java -client -XX:+PrintFlagsFinal -version | grep CICompilerCount
-XX:CICompilerCount=N
查看具体JVM数据:java -client -XX:+PrintFlagsFinal -version | grep CompileThreshold(筛选CompileThreshold参数,不筛选默认打印全部参数)
即时编译优化技术
逃逸分析
什么叫逃逸:变量会被其他线程改动
共享参数
返回值
参数
逃到哪里去
方法外
线程外
什么情况不逃逸
对象和变量作用域是局部的
所以说局部的变量和对象不会逃逸的
基于逃逸分析现象,JVM开发了三种优化技术
逃逸分析默认是开启的
1.栈上分配
对象在堆种分配
TLAB:线程私有也会分配对象
2.标量替换
对象final修饰的变量直接替换,编译优化
3.锁消除
局部变量作为锁,就会锁消除,JIT
pulic void syc(){
synchronized(new Test()){
doSomeThing()....
}
}
优化之后
pulic void syc(){
doSomeThing()....
}
即时编译器分析new Test(),不会发生逃逸,也不会存在锁竞争,所以消除了锁