Java内存模型:编译器重新排列代码行
众所周知,Java语言允许编译器重新排列已编译代码的行,只要重新排序对代码语义没有影响。但是,编译器只需要关心从当前线程看到的语义。如果这种重新排序影响多线程情况下的语义,通常会导致并发问题(内存可见性) 我的问题:Java内存模型:编译器重新排列代码行,java,memory,memory-model,Java,Memory,Memory Model,众所周知,Java语言允许编译器重新排列已编译代码的行,只要重新排序对代码语义没有影响。但是,编译器只需要关心从当前线程看到的语义。如果这种重新排序影响多线程情况下的语义,通常会导致并发问题(内存可见性) 我的问题: 允许编译器使用这个freedm实现了什么?编译器真的有可能通过重新排列代码来生成效率更高的代码吗?我还没有看到这方面的实际案例。我有时觉得,这可能带来的并发风险远远超过了好处(如果有的话) 程序员有没有办法告诉编译器不要这样重新排列行?我知道使用同步原语可以有效地处理重新排列的副作
重新排列字节码的过程由JIT(即时编译器)管理。您可以使用以下选项停止它的运行:
-Djava.compiler=NONE
请参阅此处的更多信息:Java C编译器几乎没有进行任何优化 JIT本机编译器可以在内存排序出现问题时对指令重新排序。但是,CPU也可以重新排序具有相同效果的指令和内存更新 允许编译器使用这个freedm实现了什么 主要的好处是代码的可移植性。您提供的保证越多,确保每个平台都能做到这一点就越困难 通过允许CPU在可能的时候执行指令,而不是按照严格的顺序执行指令,性能也有了显著的提高 编译器真的有可能通过重新排列代码来生成效率更高的代码吗 对。但CPU所做的重新排序更为重要 我还没有看到这方面的实际案例。我有时觉得,这可能带来的并发风险远远超过了好处(如果有的话) 程序员有没有办法告诉编译器不要这样重新排列行 这就是为什么要使用诸如
volatile
、synchronized
块和锁等内存屏障。当您使用这些工具时,您可以获得线程安全保证
我知道使用同步原语可以有效地处理重新排列的副作用,但我想问的是,是否有任何直接的方法(编译器选项)可以关闭此功能
您可以关闭JIT,但大多数重新排序都是由CPU完成的,因此不会有多大效果
避免更新的重新排序是线程安全问题的一小部分(其最大的问题是不明确且很少发生,这使得测试变得困难),一旦您编写了线程安全代码,这一问题就会得到缓解
编译器真的有可能通过重新排列代码来生成效率更高的代码吗
哦,是的
现代计算机体系结构中的一个事实是内存是主要的性能瓶颈。CPU执行寄存器到寄存器指令的速度比它对主存的读写速度快很多倍。这就是为什么高性能芯片有2到3级的内存缓存。此外,典型的CPU使用流水线允许多条指令同时执行
为了使具有这些属性的CPU获得最大的性能,编译器(以及CPU本身)需要能够对指令进行重新排序,以充分利用内存带宽,并保持流水线的满。本机代码还需要能够使用保存在寄存器中的值(变量),并尽量减少使用等待内存写入传播到主存的内存屏障指令。这些仅允许(在Java中),因为Java内存模型允许重新排序
注意:我们这里讨论的是一个显著的加速:可能是3到5倍的加速
如果您想了解具体数量,请使用实例变量密集型应用程序并重写它,以便所有实例变量都是易失的
。(这将减少重新排序的范围,并导致所有读写都进入内存。)然后根据修改后的版本对原始应用程序的性能进行基准测试。这不会停止CPU重新排序指令。;)而且它会让你的程序运行非常慢,所以这不是一个对任何生产使用都有用的选项。