当前位置: 首页 > 后端技术 > Java

Java实训:JVM锁优化与逃逸分析

时间:2023-04-01 16:00:13 Java

锁优化在加锁的过程中,jvm会采用自旋、自适应、锁淘汰、锁粗化等优化方式来提高代码执行效率。自旋锁和自适应自旋现在大部分处理器都是多核处理器。如果有多核处理器,java训练允许两个或多个线程并行执行。我们可以让等待线程不放弃处理器。执行时间处理时间。设置一个等待超时时间,看线程能否快速释放锁。在等待期间,可以执行一个空循环,让当前线程继续占用CPU时间片。这就是所谓的“自旋锁”。自旋锁可以在JVM中通过+XX:UseSpinning开启,JDK1.6之后默认开启。由于使用自旋锁会让锁竞争者占用更多的处理器时间,所以JVM为自旋次数指定了一个参数。我们可以通过-XX:PreBlockSping(默认10次)来改变它。偏向锁、轻量级锁的状态转换、对象MarkWord的关系转换如下图所示:tobesynchronizedcannothaveshareddataatruntime消除竞争锁的优化策略。锁消除的判断主要基于逃逸分析。如果你判断一段代码,堆上的所有数据都不会逃逸被其他线程访问,那么就把它当作栈上的数据,认为它们是私有的,不需要进行同步锁。下面是一个添加三个字符串x,y,z的例子,从源码上看,逻辑上都没有同步publicStringconcatStr(Stringx,Stringy,Stringz){returnx+y+z;}String是不可变类,字符的链接总是会生成一个新的String对象,所以Javac编译器会自动优化String链接,在jdk5之前会把字符串链接转成StringBuffer;jdk5之后会转为StringBuilder对象的连续append()操作。再看javac后的反编译结果:publicStringconcatStr(Stringx,Stringy,Stringz){StringBuildersb=newStringBuilder();sb.append(x);sb.append(y);sb.append(z);returnsb.toString();}再来看看javap反编译的结果:这里你可能会担心StringBuilder不是线程安全的操作会存在线程安全问题吗?这里的答案是否定的,经过“逃逸分析后”的x+y+z操作优化后,其动态作用域被限制在concatStr方法内,也就是说当前执行的StringBuilder操作在concatStr方法内部,“其他外部线程无法访问它,所以这里“虽然有锁,但可以安全地消除。所以当我们编译的时候,这段代码会忽略所有的同步措施,直接执行。”锁粗化原则一般来说,我们写代码的时候,总是建议把同步块的范围限制得尽可能小——只在范围内同步对共享数据的实际操作,这也是为了让需要同步的操作尽可能多,即使锁的竞争少,等待锁的线程也能快速获取到锁。在大多数情况下,上述原则是正确的,但是如果“一系列连续的操作重复锁定和解锁同一个对象,甚至在循环体中出现锁定操作”,那么即使没有线程竞争和频繁的互操作也会还会导致不必要的性能损失StringBufferbuffer=newStringBuffer();/*锁粗化/publicvoidappend(){buffer.append("aaa").append("bbb").append("ccc");}以上代码每次调用buffer.append方法时都需要对java培训机构进行加锁和解锁。如果JVM注册了一系列连续的对同一个对象的加锁和解锁操作,就会组合成一个更大的加锁和解锁操作,即在执行第一个append方法时加锁,加解锁在最后一个追加方法结束后执行。EscapeAnalysisEscapeAnalysis是一种跨全局函数的数据流分析算法,可以降低有效Java程序中的同步负载和内存堆分配压力。通过逃逸分析,JavaHotspot编译器可以分析出一个新的对象引用范围,并决定是否将该对象分配到堆上。“逃逸分析的基本行为是分析对象的动态范围。”方法逃逸发生在一个对象在方法被定义后,可能被外部方法引用,比如调用参数给其他方法,这种情况称为方法逃逸。线程逃逸当一个对象可能被外部线程访问时,如:赋值给其他线程访问的实例变量,这称为线程逃逸。通过逃逸分析,如果编译器对代码进行了优化,如果能够证明一个对象不会在方法或线程之外逃逸(其他线程方法或线程无法通过任何方法访问该变量),或者逃逸程度比较低(只从方法中逃逸而不从线程中逃逸),你可以对这个对象进行不同程度的优化:1.堆栈分配(StackAllocations)根本不会逃逸的局部变量和不会从线程中逃逸的对象,如果你使用堆栈分配,对象将在方法结束后自动销毁。以减轻垃圾收集器的压力。2.标量替换(ScalarReplacement)一个对象可能会被访问??而不会被存储为一个连续的存储结果,因此对象的一部分(或全部)可能不会存储在内存中,而是存储在CPU寄存器中。3.同步消除如果发现一个对象只能被一个线程访问,那么这个对象的操作就可以认为是异步的。文章来自小哈学Java