区别于JAVA线程模型
- 5大区域
程序计数器、jvm栈,本地方法栈,堆,方法区
前三个线程私有,后两个线程共享
程序计数器
执行的是本地方法的话,值为null,没有规定OOM
JVM栈
里面存储着栈帧,一个方法的执行过程就是一个栈帧的入栈和出栈
有OOM 和StackOverFlow
- 栈帧
- 包括局部变量表
变量槽为最小单位(4字节)
long和double占2个槽
可以存放八种基本类型+reference+returnAddress
编译期确定大小
完成方法参数的传递,非静态方法的第0个索引位为this
变量槽可以重用,如果没有发生覆盖,则影响GC回收 - 操作数栈
用于执行运算 - 动态链接
指向常量池中该方法的引用 - 方法返回地址
完成了异常退出,异常处理是采用本方法的异常表中没有搜到异常处理器就会抛出异常 & 退出
本地方法栈
和JVM栈一样,区别是调用的本地方法
堆
大对象存在物理连续的区域,逻辑上连续,物理上不连续
TLAB,每个线程都会申请,用于快速本地分配堆空间
会OOM
方法区
存储类型信息、常量、静态变量、即时编译器编译之后的代码
实现方式从原来的永久代,变为现在的元空间
JDK7把字符串常量池和静态变量移到堆
JDK8之后采用本地内存实现的元空间存储类型信息,运行时常量池
元空间有自己的垃圾手机机制
对象创建过程
- 定位类的符号引用
- 检查这个符号引用是否被加载、解析、初始化
- 分配内存(首先在TLAB中分配,没有了则CAS去堆里分配TLAB)
- 全都初始化为0
- 构造函数
对象头
三部分Mark Word、Klass、数组长度
Mark Word用来实现sync的锁升级过程
由于讲到了对象头,顺便看看锁优化
锁优化
- 锁消除
利用逃逸分析技术得到变量是否会竞争,如果没有则消除
return s1+s2+s3;
1.5之前会转为StringBuffer的append操作,会出现同步(StringBuffer是线程安全的)
1.5之后会转为StringBuilder的append操作
1.5之前需要锁消除
锁粗化
和锁消除一样,StringBuffer的每个append操作都会加锁,锁粗化会优化至append的开头和结尾加锁、自旋锁和适应性自旋锁
占用CPU时间,就是不进行线程切换,一直在那死循环
锁膨胀
先来一个盗图(出自https://blog.csdn.net/xueba8/article/details/88753443)
如果一个对处于偏向锁状态的对象求hash码,会使偏向锁升级为重量锁。