重排序通常是编译器或运行时环境为了优化程序性能而采取的对指令进行重新排序执行的一种手段。重排序分为两类:编译期重排序和运行期重排序,分别对应编译时和运行时环境。
在并发程序中,程序员会特别关注不同进程或线程之间的数据同步,特别是多个线程同时修改同一变量时,必须采取可靠的同步或其它措施保障数据被正确地修改,这里的一条重要原则是:不要假设指令执行的顺序,你无法预知不同线程之间的指令会以何种顺序执行。
但是在单线程程序中,通常我们容易假设指令是顺序执行的,否则可以想象程序会发生什么可怕的变化。理想的模型是:各种指令执行的顺序是唯一且有序的,这个顺序就是它们被编写在代码中的顺序,与处理器或其它因素无关,这种模型被称作顺序一致性模型,也是基于冯·诺依曼体系的模型。当然,这种假设本身是合理的,在实践中也鲜有异常发生,但事实上,没有哪个现代多处理器架构会采用这种模型,因为它是在是太低效了。而在编译优化和CPU流水线中,几乎都涉及到指令重排序。
编译期重排序
编译期重排序的典型就是通过调整指令顺序,在不改变程序语义的前提下,尽可能减少寄存器的读取、存储次数,充分复用寄存器的存储值。
假设第一条指令计算一个值赋给变量A并存放在寄存器中,第二条指令与A无关但需要占用寄存器(假设它将占用A所在的那个寄存器),第三条指令使用A的值且与第二条指令无关。那么如果按照顺序一致性模型,A在第一条指令执行过后被放入寄存器,在第二条指令执行时A不再存在,第三条指令执行时A重新被读入寄存器,而这个过程中,A的值没有发生变化。通常编译器都会交换第二和第三条指令的位置,这样第一条指令结束时A存在于寄存器中,接下来可以直接从寄存器中读取A的值,降低了重复读取的开销。
重排序对于流水线的意义
现代CPU几乎都采用流水线机制加快指令的处理速度,一般来说,一条指令需要若干个CPU时钟周期处理,而通过流水线并行执行,可以在同等的时钟周期内执行若干条指令,具体做法简单地说就是把指令分为不同的执行周期,例如读取、寻址、解析、执行等步骤,并放在不同的元件中处理,同时在执行单元EU中,功能单元被分为不同的元件,例如加法元件、乘法元件、加载元件、存储元件等,可以进一步实现不同的计算并行执行。
流水线架构决定了指令应该被并行执行,而不是在顺序化模型中所认为的那样。重排序有利于充分使用流水线,进而达到超标量的效果。
确保顺序性
尽管指令在执行时并不一定按照我们所编写的顺序执行,但毋庸置疑的是,在单线程环境下,指令执行的最终效果应当与其在顺序执行下的效果一致,否则这种优化便会失去意义。
通常无论是在编译期还是运行期进行的指令重排序,都会满足上面的原则。
Java存储模型中的重排序
在Java存储模型(Java Memory Model, JMM)中,重排序是十分重要的一节,特别是在并发编程中。JMM通过happens-before法则保证顺序执行语义,如果想要让执行操作B的线程观察到执行操作A的线程的结果,那么A和B就必须满足happens-before原则,否则,JVM可以对它们进行任意排序以提高程序性能。
volatile
关键字可以保证变量的可见性,因为对volatile
的操作都在Main Memory中,而Main Memory是被所有线程所共享的,这里的代价就是牺牲了性能,无法利用寄存器或Cache,因为它们都不是全局的,无法保证可见性,可能产生脏读。
volatile
还有一个作用就是局部阻止重排序的发生,对volatile变量的操作指令都不会被重排序,因为如果重排序,又可能产生可见性问题。在保证可见性方面,锁(包括显式锁、对象锁)以及对原子变量的读写都可以确保变量的可见性。但是实现方式略有不同,例如同步锁保证得到锁时从内存里重新读入数据刷新缓存,释放锁时将数据写回内存以保数据可见,而volatile变量干脆都是读写内存。
相关推荐
Java中的JVM重排序是一种优化策略,用于提升程序性能,主要分为编译期重排序和运行期重排序。重排序的目的是在不改变程序逻辑的情况下,优化指令的执行顺序,以充分利用硬件资源,如寄存器和CPU流水线。 编译期重...
在这个模型中,内存屏障(Memory Barrier)和重排序(Reordering)是两个关键概念,它们对并发编程的正确性和性能有着重要影响。 **重排序** 重排序是指编译器和处理器为了优化程序性能,可能会改变程序执行顺序的...
JMM通过内存屏障来保证指令重排序不会破坏可见性和原子性。 【Java运行时数据区】 Java运行时数据区包括程序计数器、Java虚拟机栈、本地方法栈、堆内存和方法区(在Java 8后改为元空间)。每个线程都有独立的程序...
有序性保证线程可以按照源代码的顺序执行,尽管虚拟机可能会对指令进行重排序,但是对重排序后的结果需要进行正确的同步,以保证程序的有序性。 #### 内存间交互操作 JMM定义了若干规则来保证线程间的交互操作,...
Java指令重排序是Java虚拟机(JVM)和CPU的优化机制,目的是为了提高程序的执行效率。该机制可以在虚拟机层面和硬件层面对指令进行重新排序,以充分利用CPU的资源。 虚拟机层面的指令重排序是为了减少内存操作速度...
6JVM内存模型的相关知识了解多少,比如重排序,内存屏障,happen-before,主内存,工作内存 7.简单说说你了解的类加载器,可以打破双亲委派么,怎么打破 8.说说你知道的几种主要的JVM参数 9.怎么打出线程栈信息 10....
在Java中,我们可以使用多种算法,如排序(快速排序、归并排序、冒泡排序等)、查找(二分查找、哈希查找等)、图算法(Dijkstra、Floyd等)和动态规划等。理解和熟练运用这些算法,可以显著提高代码的效率和质量。 ...
2019最新深入理解JVM内存结构及运行原理(JVM调优)高级核心课程视频教程... 第107讲 重排序问题 00:23:19 第108讲 锁的内存语义 00:13:54 第109讲 volatile的内存语义 00:12:04 第110讲 final域内存语义
CPU的内存模型通常分为强内存模型和弱内存模型,区别在于是否允许对主内存的修改立即可见以及是否允许编译器内存访问指令的重排序。大多数现代处理器采用弱内存模型。 JMM借鉴了CPU-缓存-主内存的模型,即线程对...
在Java编程语言中,数组排序是一项基础且重要的任务。它涉及到不同的算法,这些...在实际应用中,还可以考虑使用Java的内置排序方法`Arrays.sort()`,它使用了一种高效的快速排序变体,但具体实现细节则由JVM实现决定。
在并发处理方面,Java 7提供了Fork/Join框架,这是基于工作窃取算法的并行执行模型,适用于处理大规模的计算任务,如数组排序。这个框架极大地提升了多核处理器环境下Java程序的性能。 JVM7还加强了对动态语言的...
在Java中,可以使用Collections框架进行排序,比如快速排序和归并排序;对于搜索,可以使用二分查找法等提高效率。此外,理解和编写高效的算法能够帮助解决复杂问题,提升程序性能。 JVM(Java Virtual Machine)是...
首先,Android系统基于Java虚拟机(JVM),因此可以使用Java的所有特性。在Java中,我们可以使用`Collections.sort()`方法对List对象进行排序,但这个方法默认按照Unicode编码排序,对于中英文混合的情况并不适用。...
这个方法使用了快速排序或归并排序算法,具体取决于元素类型和JVM实现。要对自定义对象进行排序,你需要确保你的类实现了Comparable接口或者提供一个Comparator对象来定义比较规则。 ```java public class MyObject...
6. **volatile关键字**:理解其保证可见性和禁止指令重排序的作用。 7. **线程安全**:了解线程不安全的情况,如非线程安全的Singleton实现,以及如何使其线程安全。 8. **中断和异常处理**:Thread.interrupt()...
- `-s` 或 `--sort`:按照指定字段排序输出,如CPU使用率、内存占用等。 - `-f` 或 `--follow`:持续关注某个线程,直到线程结束。 四、实战应用 在实际工作中,vjtop可以用于以下场景: 1. **性能调优**:当发现...
5. `.class`文件:这是编译后的Java字节码文件,用于在JVM(Java虚拟机)上执行。 6. 可能还包含一些资源文件,如图片或样式表(CSS),用于增强用户体验。 通过这个蓝图,学习者不仅可以了解到基本的Java编程概念...
熟悉这些数据结构在实际编程中的应用场景,例如在排序、搜索、图遍历等问题上的应用。 5. **Zookeeper**:Zookeeper是分布式协调服务,常用于管理集群配置、命名服务、分布式同步、组服务等。理解Zookeeper的基本...
重排序是指JVM在执行Java程序时,对指令的重新排序,以提高程序的执行效率。重排序可以发生在单线程环境中,也可以发生在多线程环境中。当重排序发生时,可能会导致程序的执行结果与原始顺序不同。 例如,在下面的...