编译器可以在变量实际启动之前就为变量赋值吗?

问题描述 投票:0回答:2

我刚刚读过 http://www.javaworld.com/javaworld/jw-04-2003/jw-0425-designpatterns.html?page=5 它说:

编译器可以自由地给单例成员变量赋值 在单例的构造函数之前 叫

我想知道这是不是一个错字。他们真的想说:JVM 的实现是自由的,而不是编译器是自由的

我的第二个问题是 C#/VB 也有这个问题吗? (其中“编译器”可以自由地为变量赋值,甚至在变量完全启动之前/甚至在变量类的构造函数完全运行之前。

java c# vb.net initialization
2个回答
4
投票

在Java中,为对象分配内存和调用构造函数是两个独立的操作。例如,类似

Object o = new Object();

编译成这些字节码:

0:  new #2; //class java/lang/Object
3:  dup
4:  invokespecial   #1; //Method java/lang/Object."<init>":()V
7:  astore_1

指令 0 之后,对已分配但未构造的对象的引用位于堆栈上。直到偏移量 4 才会调用构造函数。绝对没有什么可以阻止编译器将该引用分配给它想要的任何变量,包括静态成员。因此这篇文章是正确的。

我不知道 CLR 字节码,但我想它与 JVM 的指令集相当接近,而且我猜想该运行时也存在同样类型的与线程相关的警告。它当然适用于本机代码编译器。


1
投票

问题第一部分的答案是你是正确的,尽管这更多的是一个草率的术语而不是拼写错误或错误。 (简单地说,编译器不会为变量赋值……这只在编译器生成的代码被执行时才会发生。)

技术上更准确的重述是:

“...因为编译器可以自由生成代码,可能导致在调用单例构造函数之前将单例成员变量的值写入内存,并且构造的对象已刷新到内存。”

这种事情最有可能发生在本机代码编译器级别,当编译器(合法地)重新排序指令时,或者仅仅是内存流水线的结果。 Java 内存模型特别允许这样做,以便编译器能够生成在多核计算机上运行速度更快的代码。 (另一方面,您的多线程代码必须以所需的方式同步,否则它可能不可靠。)

(理论上字节码编译器也可以重新排序字节码,但很可能不会。字节码编译器进行细粒度优化没有什么价值。事实上,它可能是有害的,因为它可能会导致对于 JIT 编译器的优化器来说更困难。)


我会将 C# 和 VB 案例留给熟悉这些语言规范的人。

© www.soinside.com 2019 - 2024. All rights reserved.