jvm读书笔记

类加载与链接:

1.类型的唯一性通过类的全限定名和类的定义类加载器来标识。每个类加载器维护一个独立的命名空间,只有在同一命名空间中的类才能相互调用。一般的jvm中共有三个类加载器:启动类加载器 扩展类加载器 系统类加载器。 自定义类由系统类加载器加载,ArrayList由启动类加载器加载,假如在自定义类中初次使用ArrayList,此时系统类加载器的命名空间中并没有ArrayList,因此需要借双亲委派机制找到启动类加载器,在这一过程中扩展类加载器均系统类加载器被标记为ArrayList初始类加载器,他们直接或间接的向ArrayList的定义类加载器请求ArrayList类型在内存中的直接引用,同时ArrayList也被加入他们各自的命名空间中。后续自定义类再次使用ArrayList时,会发现ArrayList已经在当前类加载器(当前执行代码所在类的定义类加载器,也就是系统类加载器)的命名空间中,此时可直接得到ArrayList类型的直接引用,并替换掉常量池中的符号引用。

2.每个类的常量池中都有该类字面量存放的地方,同时整个jvm也维护了一个string列表。在解析类中字面量后,会在string列表中生成代表字面量的string对象,而类常量池中的字面量被修改为指向对应string对象的直接引用。也可通过string的intern()方法将任意不在上述列表中的string添加到上述列表中。

3.通过子类访问父类的类变量时,不会触发子类的初始化,因为这里只需要父类类变量的直接引用,只会导致父类的初始化;只有访问接口的成员变量时才会导致接口的初始化,理由同上。

字节码执行:

1.在调用一个实例方法时,会将实例对象的引用以及参数存入下一个栈帧的局部变量表中,实例对象的引用(也就是this指针)位于索引0处,其他参数依次往后存放。为什么不存入操作数栈呢?因为每次只能访问操作数栈顶的元素,显然不适合将这些随时可能访问的变量放到栈中。这里也可以看出方法传入的参数与方法体中定义的局部变量地位是一样的,他们在超过自己的作用域之后都会让出局部变量表中的槽位,让槽位可以被重用,因此在循环中定义局部变量并不需要无限大的空间。另外jvm可以让调用方的操作数栈和被调用方的局部变量表重合,这样就省去了复制变量的过程。

 

内存管理:

1.若对象只在某个方法中能访问,可以将其拆成一个个标量(基本类型)在局部变量表中分配,随着出栈帧而自动回收,否则在TLAB中访问,每个线程都有自己的TLAB,目的仅仅是避免每次分配对象时都有锁住整个堆,并不意味着线程不能访问其他线程的TLAB,毕竟是在堆中,只要能拿到对象的引用就可以访问。TLAB一般不大,用完会线程会申请新的TLAB,老的TLAB作为正常堆的一部分。

2.对于class中的string字面量,运行之后会被解析并替换成指向相应string对象的直接引用。多个class文件中若有相同的字面量,则会被解析成同一个string对象引用,这些string对象的引用被jvm统一收集在字符串常量池中,通过intern()方法也可以将原本不在字符串常量池中的string对象引用放进字符串常量池。

 

锁:

1.当某对象因monitorenter字节码而被轻量级锁定时,在对应线程栈上会记录该对象的markword,称之为lock record(具体在栈帧的哪个位置有待研究),后续锁重入时会在lock record中不断记0,0的数量代表重入的次数

2.锁偏向某线程时,对应线程ID会覆盖原先存放hashcode的位置,也就是说在偏向模式下锁对象的hashcode是无法保存的,这里的hashcode指obj最原始的hash值,如果锁对象的hashcode()方法被重写过则不受偏向模式的影响,否则直接访问锁对象原始的hash值时会撤销(revoke)偏向并且标记对象为不可偏向

3.相对于轻量级锁,偏向锁一旦偏向了某个线程,那么后续加锁解锁不再需要CAS操作,而轻量级锁每次加/解锁都需要CAS操作(根据MESI协议,CAS可能会强制刷新cpu的cache store buffer和invalidated queue,会浪费较多的cpu指令周期)

4.jvm会以类(class)为单位,对类中所有对象作为偏向锁撤销时付出的代价进行统计,这个代价包括在安全点(safepoint)暂停偏向的线程,在线程栈中找到锁记录(lock record)并修改为轻量级锁时的锁记录(不像轻量级加锁时有CAS操场支持,这里应该包含多条指令,需要更新当前持有锁线程的栈信息,因为需要等STW)。若代价超过某一阈值,则对该类中的所有对象进行批量重偏向(bulk rebias),此时jvm会扫描线程栈中的锁记录并找出该类所有被锁住的对象(从锁记录可以看出偏向的线程是否仍然持有锁),以同样值更新他们markword中的epoch值和类的epoch值,而那些未加锁的对象此刻的epoch值已与类的epoch值不相等,意味着这些对象的锁已不再偏向任何线程,处于可偏向但未偏向状态,下次他们可以偏向其他线程,仍可享受第3点中提到的偏向模式的好处。批量重偏向后还是会监控该类对象后续偏向锁的撤销情况,并由此决定是否进行批量锁撤销(以class为单位标记不适合偏向模式,后续加锁直接走轻量级锁)。

小结:

重量级锁:有同时竞争/需排队
轻量级锁,无同时竞争,但每次获取锁均需要cas,会刷新cpu的cache buffer
偏向锁,相对轻量级锁减少CAS操作,适合于锁的持有线程一直是同一个的情况。以class为单位判断是否适合这种模式,若锁持续被不同的线程获取,置class下面所有的object为不适合偏向锁

参考:Why does keeping biased locking state conflict with keeping the identity hash code?

Eliminating Synchronization-Related Atomic Operations with Biased Locking and Bulk Rebiasing

How does the default hashCode() work?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值