The support for fields can be separated into three categories: support for private (or even protected) fields, support for non-private fields, and support for various kinds of access modifiers.
Private fields can only be referenced from within the defining class. Thus they do not require any special care for binary compatibility. Protected fields can only be referenced from the defining class or a subclass. In this case, the loading of the referencing class cannot precede the loading of the defining class. We can completely patch the references to these fields during class loading because by that time the layout of these fields is already determined. By doing this, we do not have to go through the extra indirection of the offset tables.
Changing non-private fields may affect other classes that depend on them. A similar technique as we used for methods can be used here, though it may be relatively less efficient. However, it is generally good software engineering practice to limit the use of non-private fields. Using non-private fields is also discouraged in the Binary Compatibility chapter of the JLS; to quote from Section 13.4.7 of JLS [12], ``Widely distributed programs should not expose any fields to their clients.'' In fact, non-private fields (especially as part of public APIs) seem to be quite rare. To the authors' knowledge, they are almost non-existent in the standard Java libraries. We believe that the inefficiency here will not have much impact in practice.
Nevertheless, the handling of removed fields is tricky. Unlike calling a method, the trick with reserving the 0-offset entry will not work in this case because accessing it as a field will not raise any exceptions. Using a run-time check for every field access to determine whether the offset for a field is valid has too great a cost in performance. Our solution is, instead of detecting missing fields lazily, to raise exceptions at class loading time when trying to fill in an offset table entry for a field, if the corresponding information is not in the global allocation table. Note that this solution does not obey the JLS on the particular aspect that exceptions of a missing field should be raised lazily. For full compliance with the JLS, one possible solution is to fill in the offset table entry of the missing field with some special offset which triggers an OS trap when accessed. A similar technique is introduced by Joisha et al. [17] for the IBM Quicksilver quasi-static compiler [27] for a different purpose, namely to trigger the ``stitching'' (or linking) operations.
More surprisingly, adding a field is not always a compatible change if changes of modifiers are involved. We discuss this peculiarity in Section 4.4.3 together with other modifier changes.