实战Java虚拟机笔记 7-8

不变性可以提高多线程访问的性能。因为对象不可变,因此对于所有线程都是只读的,多线程访问时,即使不加同步也不会产生数据的不一致,故减小了系统开销。

浅堆:表示一个对象结构所占用的内存大小。
深堆:表示一个对象被GC回收后,可以真实释放的内存大小。
实际大小:表示一个对象所能触及的所有对象的浅堆大小之和。与深堆相比,似乎这个在日常开发中更为直观和被人接受,但实际上,这个概念和垃圾回收无关。

支配树体现了对象实例间的支配关系。在对象引用图中,所有指向对象B的路径都经过对象A,则认为对象A支配对象B。如果对象A是离对象B最近的一个支配对象,则认为对象A为对象B的直接支配者。支配树是基于对象间的引用图所建立的,它有以下基本性质:
1. 对象A的子树(所有被对象A支配的对象集合)表示对象A的保留集(retained set),即深堆。
2. 如果对象A支配对象B,那么对象A的直接支配者也支配对象B。
3. 支配树的边与对象引用图的边不直接对应。

OQL:
Select子句:
1. 可以使用*,查看结果对象的引用实例。
select * from java.util.Vector v
2. 可以指定对象的属性进行输出,使用"OBJECTS"关键字,可以将返回结果集中的项以对象的形式显示。
select objects v.elementData from java.util.Vector v

From子句:
可以指定类名、正则表达式,可以查询某个类的所有子类实例。
select * from java.lang.String s
select * from ".*Stu.*"
select * from instanceof java.util.AbstractCollection
可以查询实本身的信息
select * from objects java.lang.String

OQL中可以访问堆内对象的属性,也可以访问堆内代理对象的属性。
select toString(f.path.value) from java.io.File f
select s.@retainedHeapSize from java.lang.String s

在Java虚拟机的实现中每个对象都有一个对象头,用于保存对象的系统信息。对象头中有一个称为Mark Word的部分,它是实现锁的关键。在32位系统中,Mark Word为一个32位的数据,在64位系统中,它占64位,它是一个多功能的数据区,可以存放对象的哈希值、对象年龄、锁的指针等信息。

偏向锁:
偏向锁是JDK1.6提出的一种锁优化方式。其核心思想是,如果程序没有竞争,则取消之前已经取得锁的线程同步操作。也就是说,若某一锁被线程获取后,便进入偏向模式,当线程再次请求这个锁时,无需再进行相关的同步操作,从而节省了操作时间。如果在此之间有其他线程进行了锁请求,则锁退出偏向模式。在JVM中使用-XX:+UseBiasedLocking可以设置启用偏向锁。
当该线程再次尝试获得锁时,通过Mark Word的线程信息就可以判断当前线程是否持有偏向锁。偏向锁在少竞争的情况下,对系统性能有一定帮助。

轻量级锁:
使用轻量级锁时,不需要申请互斥量,仅仅将Mark Word中的部分字节CAS更新指向线程栈中的Lock Record,如果更新成功,则轻量级锁获取成功,记录锁状态为轻量级锁;否则,说明已经有线程获得了轻量级锁,目前发生了锁竞争(不适合继续使用轻量级锁),接下来会发生锁膨胀。

锁膨胀后,进入ObjectMonitor的enter(),线程很可能会在操作系统层面被挂起,这样线程上下文切换的性能损失就比较大。因此,在锁膨胀之后,虚拟机会做最后的争取,希望线程可以尽快进入临界区而避免被操作系统挂起,一种较为有效的手段就是使用自旋锁。
自旋锁可以使线程在没有取得锁时 ,不被挂起,而转而去执行一个空循环(即所谓的自旋),在若干个空循环后,线程如果可以获得锁,则继续执行。若线程依然不能获得锁,才会被挂起。
在JDK1.7中,自旋锁的参数被取消,虚拟机不再支持由用户配置自旋锁。自旋锁总是会执行,自旋次数也由虚拟机自行调整。

锁消除是Java虚拟机在JIT编译时,通过对运行上下文的扫描,去除不可能存在共享资源竞争的锁。通过锁消除,可以节省毫无意义的请求锁时间。


锁在应用层的优化思路:
1. 减少锁持有时间。如果线程持有锁的时间很长,那么相对的,锁的竞争程度也就越激烈。因此,在程序开发过程中,应该尽可能地减少对某个锁的占有时间,以减少线程间互斥可能。
2. 减小锁粒度,就是指缩小锁定对象的范围,从而减少锁冲突的可能性。例如ConcurrentHashMap的put方法。ConcurrentHashMap将整个HashMap分成若干个段(Segment),每个段都是一个子HashMap.
3. 锁分离,是减小锁粒度的一个特例,它根据应用程序的功能特点,将一个独占锁分成多个锁,一个典型的案例就是java.util.concurrent.LinkedBlockingQueue的实现。
4. 锁粗化,为了保证多线程间的有效并发,会要求每个线程持有锁的时间尽量短,即在使用完公共资源后,应该立即释放锁。只有这样,等待在这个锁上的其他线程才能尽早地获得资源执行任务。但是,凡事都有一个度,如果对同一个锁不停地进行请求、同步和释放,其本身也会消耗系统宝贵的资源,反而不利于性能的优化。


无锁:
CAS:CAS算法(Compare And Swap)的过程是这样:它包含3个参数(V,E,N)。V表示要更新的变量,E表示预期值,N表示新值。仅当V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做了更新,则当前线程什么都不做。最后,CAS返回当前V的真实值。
原子操作:为了能让CAS操作被Java应用程序充分使用,在JDK的java.util.concurrent.atomic包下,有一组使用无锁算法实现的原子操作类,主要有AtomicInteger, AtomicIntegerArray, AtomicLong, AtomicLongArray和AtomicReference等。

Java内存模型:
1. 原子性,在32位Java虚拟机系统中,对于long和double的赋值读取,由于long和double长度为64位,无法一次性操作,因此对于他们的操作都不是原子的,在并发环境下,可能会出现一些意想不到的错误。可以将他们定义为volatile解决。
2. 有序性,CPU可能会对目标指令进行重排。在一个线程中观察另外一个线程的操作,我们会发现,被观察线程的指令顺序和预期情况不符。可以在方法上添加synchronized关键字解决。
3. 可见性,可见性是指当一个线程修改了一个变量的值,在另外一个线程中可以马上得知这个修改。部分变量的值可能会放入到寄存器或高速缓冲(Cache)中,而每个CPU都拥有独立的寄存器和Cache,从而导致其他线程无法立即发现这个修改。可以使用volatile关键字解决。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值