深入JVM内核——原理、诊断与优化

来源:互联网 发布:python 判断类型相同 编辑:程序博客网 时间:2024/06/03 04:43

偏向锁

1、大部分是没有竞争的情况,所以通过偏向锁来提高性能
2、即锁会偏向与当前占有锁的线程
3、将对象头Mark的标记设置为偏向,并将占有锁线程ID写入对象头Mark
4、当其他线程请求相同的锁时,偏向模式结束
5、-XX:+UseBiasedLocking
-默认启动

轻量级锁

1、比普通锁高一个级别,快速锁定方法
2、如果对象没有被锁定
–将对象的Mark指针保存到锁对象
–将对象设置为指向锁的指针(在线程栈空间中)

自旋锁

1、当竞争存在时,如果线程可以很快获得锁,那么可以不在OS层挂起线程(性能损耗严重),让线程做几个空操作(自旋转)
2、1.7中,内置默认开启自旋锁
3、如果同步块很长,自旋失败,会减低系统性能
4、如果同步块很短,自旋成功,节省线程挂起时间,提高性能

以上三种锁是内置于JVM中的获得锁(不是Java语言层面的)的优化方法和获得锁的步骤

偏向锁 -> 轻量级锁 -> 自旋锁 -> 普通锁
上面意思是首先尝试获得偏向锁,然后在也可尝试轻量级锁,两种失败了就尝试自旋锁,如果自旋锁也失败,就尝试普通锁了

ClassLoader默认设计模式-协同工作

这里写图片描述

1、(从App ClassLoader)自底向上检查类是否已经加载
2、(Bootstrap ClassLoader)自顶向下尝试加载类

这里写图片描述
这里写图片描述

1、方法区(由JVM维护)
保存装载的类信息
* 类型的常量池
* 字段,方法信息
* 方法字节码
通常和永久区联系在一起(保存相对稳定的信息)

2、堆内存(全局共享)
与程序开发密切相关
3、Java栈(线程私有)
栈由一系列帧组成(也可以叫帧栈)
每一个方法调用创建一个帧,并压入栈

(1)Java栈——局部变量表,包含参数和局部变量
这里写图片描述
局部变量表就一个帧
(2)Java栈——操作数栈
因为Java没有寄存器,所有参数传递使用操作栈

这里写图片描述
(2)Java栈——栈上分配
* 小对象(一般几十个bytes),在没有逃逸的情况下,可以直接分配在栈上
* 直接分配在栈上,可以直接自动回收,减轻GC压力
* 大对象或逃逸对象无法栈分配
这里写图片描述

 问题:为了能让递归方法调用的次数更多一些,应该怎么做呢?
 后来人们发现,对于该递归而言,一些压栈操作并无必要,递归中的子问题规模几乎不变,每次只减去了1或者2。如果画一个递归树,会发现很多相同的子树!!!说明该实现浪费了很多内存和时间,当解决Fn-1问题时,计算了Fn-2和Fn-3,解决Fn问题时,计算了Fn-1和Fn-2,实际上我只需要计算一次Fn-2就ok了。优化——使用自底向上的算法:线性递归

JVM内存模型
* 每一个线程有一个工作内存和主内存
* 工作内存存放主存中变量的值的拷贝
可见性
* 一个线程修改了变量,其他线程可以立即知道
保证可见性的方法
* volatile
* synchronized(ublock之前,写变量值回主存)
* final(一旦初始化完成,其他线程就可见)
有序性
* 在本线程,操作是有序的
* 在线程外观察,操作都是无序的(指令重排 或 主内存同步延时)
这里写图片描述
在reader()中即使flag是true,也不能保证a=1,因为在另一个线程(线程外),操作是无序的,flag=true也可以执行先于a=1

用锁来保证线程安全,在线程外,操作是无序的,但这没关系,因为该线程获得锁,其他线程进不来,要等其全部操作都执行完才可以执行
这里写图片描述

这里写图片描述

解释运行(读一句执行一句)
编译运行(将字节码编译成机器码,直接执行机器码)

Stop-The-World
* Java一种全局暂停的现象
* 全局停顿,所有Java代码停止,native代码可以执行,但不能和JVM交互
* 多半由于GC引起(还有可能是Dump线程、死锁检查、堆Dump)

原创粉丝点击