JEP draft: Warn about use of Memory-Access Methods in `sun.misc.Unsafe`
Author | Ron Pressler & Alex Buckley |
Owner | Ron Pressler |
Type | Feature |
Scope | JDK |
Status | Submitted |
Component | core-libs |
Discussion | jdk dash dev at openjdk dot org |
Effort | S |
Duration | S |
Relates to | JEP 471: Deprecate the Memory-Access Methods in sun.misc.Unsafe for Removal |
Reviewed by | Alan Bateman |
Created | 2024/10/14 17:42 |
Updated | 2024/10/29 15:38 |
Issue | 8342077 |
Summary
Issue a warning at run time if any memory-access method in sun.misc.Unsafe
is called. All memory-access methods were terminally deprecated in JDK 23. We strongly encourage library developers to migrate from sun.misc.Unsafe
to supported replacements, so that applications can migrate smoothly to modern JDK releases.
History
This JEP is the successor to JEP 471, which deprecated the memory-access methods in sun.misc.Unsafe
for removal in a future release and described a gradual process of removal. The Goals, Non-Goals, Motivation, and Risks and Assumptions section of this JEP are essentially identical to that of JEP 471 but are reproduced here for the reader’s convenience.
Goals
- Prepare the ecosystem for the removal of the memory-access methods in
sun.misc.Unsafe
in a future JDK release. - Make developers aware when their applications rely, directly or indirectly, on the memory-access methods in
sun.misc.Unsafe
.
Non-Goals
- It is not a goal to remove the
sun.misc.Unsafe
class entirely. A small number of its methods are not used for memory access; these will be deprecated and removed separately. - It is not a goal to change other
sun.*
classes in thejdk.unsupported
module.
Motivation
The sun.misc.Unsafe
class was introduced in 2002 as a way for Java classes in the JDK to perform low-level operations. Most of its methods — 79 out of 87 — are for accessing memory, either in the JVM's garbage-collected heap or in off-heap memory, which is not controlled by the JVM. As the name of the class suggests, these memory-access methods are unsafe: They can lead to undefined behavior, including JVM crashes. Therefore, they were not exposed as a standard API. They were neither envisaged for use by a broad range of clients nor intended to be permanent. Rather, they were introduced with the assumption that they were exclusively for use within the JDK, and that callers within the JDK would perform exhaustive safety checks before using them, and that safe standard APIs for this functionality would eventually be added to the Java Platform.
However, with no way in 2002 to prevent sun.misc.Unsafe
from being used outside the JDK, its memory-access methods became a handy tool for library developers who wanted more power and performance than standard APIs could offer. For example, sun.misc.Unsafe::compareAndSwap
can perform a CAS (compare-and-swap) operation on a field without the overhead of the java.util.concurrent.atomic
API, while sun.misc.Unsafe::setMemory
can manipulate off-heap memory without the 2GB limitation of java.nio.ByteBuffer
. Libraries that do rely on ByteBuffer
to manipulate off-heap memory, such as Apache Hadoop and Cassandra, use sun.misc.Unsafe::invokeCleaner
to improve efficiency by deallocating off-heap memory promptly.
Unfortunately, not all libraries are diligent at performing safety checks before calling the memory-access methods, so there is a risk of failures and crashes in applications. Some uses of the methods are unnecessary, driven by the ease of copy-and-paste from online forums. Other uses of the methods may cause the JVM to disable optimizations, resulting in worse performance than if ordinary Java arrays had been used. Nevertheless, because use of the memory-access methods is so widespread, sun.misc.Unsafe
was not encapsulated alongside other low-level APIs in JDK 9 (JEP 260). It remains available out-of-the-box in JDK 22, pending the availability of safe supported alternatives.
Over the past several years, we have introduced two standard APIs that are safe and performant replacements for the memory-access methods in sun.misc.Unsafe
:
java.lang.invoke.VarHandle
, introduced in JDK 9 (JEP 193), provides methods to safely and efficiently manipulate on-heap memory, i.e., fields of objects, static fields of classes, and elements of arrays.java.lang.foreign.MemorySegment
, introduced in JDK 22 (JEP 454), provides methods to safely and efficiently access off-heap memory, sometimes in cooperation withVarHandle
.
These standard APIs guarantee no undefined behavior, promise long-term stability, and have high-quality integration with the tooling and documentation of the Java Platform (examples of their use are given below). Given the availability of these APIs, it is now appropriate to deprecate and eventually remove the memory-access methods in sun.misc.Unsafe
.
Removing the memory-access methods in sun.misc.Unsafe
is part of a long-term coordinated effort to ensure that the Java Platform has integrity by default. Other initiatives include placing restrictions on the Java Native Interface (JNI, JEP 472) and on the dynamic loading of agents (JEP 451). These efforts will make the Java Platform more secure and more performant. They will also reduce the risk of application developers becoming trapped on older JDK releases due to libraries that break on newer releases when unsupported APIs are changed.
Description
We are deprecating and removing the memory-access methods in sun.misc.Unsafe
in phases:
-
Phase 1. In JDK 23, we deprecated all of the memory-access methods for removal. This caused compile-time deprecation warnings for code that refers to the methods, alerting library developers to their forthcoming removal.
-
Phase 2. In JDK NN, we will issue a warning at run time if any memory-access method is used, whether directly or via reflection. This will alert application developers and users to the forthcoming removal of the methods, and the need to upgrade libraries. At most one warning is issued regardless of which memory-access methods are used and how many times any particular method is used. The warning looks like this:
WARNING: A terminally deprecated method in sun.misc.Unsafe has been called WARNING: sun.misc.Unsafe::setMemory has been called by com.foo.bar.Server (file:/tmp/foobarserver/thing.jar) WARNING: Please consider reporting this to the maintainers of com.foo.bar.Server WARNING: sun.misc.Unsafe::setMemory will be removed in a future release
-
Phase 3. In JDK 26 or later, we will throw an exception when a memory-access method is used, whether directly or via reflection. This will further alert application developers and users to the imminent removal of the methods.
-
Phase 4. In releases after JDK 26, we will remove memory-access methods which have had standard replacements since JDK 9 in 2017.
-
Phase 5. In releases after JDK 26, we will remove memory-access methods which have only had standard replacements since JDK 22 in 2023.
For a complete list of the memory-access methods and their standard replacements, see JEP 471.
Understanding the use of memory-access methods in sun.misc.Unsafe
The vast majority of Java developers do not use sun.misc.Unsafe
explicitly in their own code. However, many applications depend, directly or indirectly, on libraries that use the memory-access methods in sun.misc.Unsafe
. In JDK 23 and later, application developers can assess how libraries are affected by the deprecation and removal of these methods by running with a new command line option, --sun-misc-unsafe-memory-access={allow|warn|debug|deny}
. This option is similar, in spirit and in form, to the --illegal-access
option introduced by JEP 261 in JDK 9. It works as follows:
-
--sun-misc-unsafe-memory-access=allow
allows use of the memory-access methods with no warnings at run time.This mode was the default in JDK 23.
-
--sun-misc-unsafe-memory-access=warn
allows use of the memory-access methods, but issues a warning on the first occasion that any memory-access method is used, whether directly or via reflection. That is, at most one warning is issued regardless of which memory-access methods are used and how many times any particular method is used.This mode is the default in JDK NN.
-
--sun-misc-unsafe-memory-access=debug
allows use of the memory-access methods, but issues a one-line warning and a stack trace on every occasion that any memory-access method is used, whether directly or via reflection. -
--sun-misc-unsafe-memory-access=deny
disallows use of the memory-access methods by throwing anUnsupportedOperationException
on every occasion that any such method is used, whether directly or via reflection.
The default value of the --sun-misc-unsafe-memory-access
option is changed from release to release as we proceed through the phases described above:
-
allow
was the default value in phase 1 (JDK 23). -
warn
is the default value in phase 2 (JDK NN), as if every invocation of thejava
launcher includes--sun-misc-unsafe-memory-access=warn
. It is possible to revert the value fromwarn
toallow
in JDK NN, thus avoiding the warning. That is, thejava
launcher can be invoked with--sun-misc-unsafe-memory-access=allow
. -
deny
will be the default value in phase 3 (JDK 26 or later). It will be possible in phase 3 to revert the value fromdeny
towarn
in order to receive a warning rather than exceptions. It will not be possible to useallow
to avoid the warning. -
In phase 5, when all of the memory-access methods have been removed,
--sun-misc-unsafe-memory-access
will be ignored. Eventually it will be removed.
Developers can also use JDK Flight Recorder (JFR) to identify when memory-access methods are used. When JFR is enabled on the command line, a jdk.DeprecatedInvocation
event is recorded whenever a terminally deprecated method is invoked. This event can be used to identify uses of the memory-access methods in sun.misc.Unsafe
. For example, here is how to create a JFR recording and then display the jdk.DeprecatedInvocation
events:
$ java -XX:StartFlightRecording:filename=recording.jfr ...
$ jfr print --events jdk.DeprecatedInvocation recording.jfr
Further details on this event and its limitations can be found in the JDK 22 release note.
Risks and Assumptions
-
Over the years, methods in
sun.misc.Unsafe
that are unrelated to memory access have been deprecated for removal after standard replacements were introduced, and many of them have already been removed:sun.misc.Unsafe::defineClass
was removed in JDK 11 afterjava.lang.invoke.MethodHandles.Lookup::defineClass
was introduced in JDK 9.sun.misc.Unsafe::defineAnonymousClass
was removed in JDK 17afterMethodHandles.Lookup::defineHiddenClass
was introduced in in JDK 15.sun.misc.Unsafe::{ensureClass,shouldBe}Initialized
were removed in JDK 22 afterMethodHandles.Lookup::ensureInitialized
was introduced in JDK 15.- Six miscellaneous methods in
sun.misc.Unsafe
were deprecated for removal in JDK 22 after standard replacements were available.
We have seen very little impact in the Java ecosystem from the removal of these relatively obscure methods. However, the memory-access methods are much better known. This proposal assumes that removing them will impact libraries. Accordingly, for maximum visibility, we are proposing their deprecation and removal via the JEP process rather than simply with a CSR request and a release note.
-
This proposal assumes that library developers will migrate from unsupported methods in
sun.misc.Unsafe
to supported methods injava.*
.We recommend in the strongest possible terms that library developers do not migrate from unsupported methods in
sun.misc.Unsafe
to unsupported methods found elsewhere inside the JDK.Library developers who ignore this recommendation will force their users to run with
--add-exports
or--add-opens
options on the command line. This is not merely inconvenient, but risky: JDK internals can change from release to release without notice, breaking libraries which depend on the internals, thereby breaking applications which depend on the libraries. -
A risk of this proposal is that some libraries use the on-heap memory-access methods of
sun.misc.Unsafe
in ways that cannot be replicated by the standard APIs available in JDK 23. For example, a library may useUnsafe::objectFieldOffset
to obtain the offset of a field in an object, then useUnsafe::putInt
to write anint
value at that offset regardless of whether the field is anint
. The standardVarHandle
API cannot examine or manipulate objects at such a low level because it refers to fields by name and type, not by offset.Use cases that rely on field offsets are, in effect, revealing or exploiting implementation details of the JVM. In our view, such use cases do not need to be supported by a standard API.
-
A library may use
UNSAFE.getInt(array, arrayBase + offset)
to access array elements in the heap without bounds checking. This idiom is typically used for random access, since sequential access to array elements, whether via ordinary array-index operations theMemorySegment
API, already benefits from the JIT's bounds-check elimination.In our view, random access to array elements without bounds checking is not a use case that needs to be supported by a standard API. Random access via array-index operations or the
MemorySegment
API has a small loss of performance compared to the on-heap memory access methods ofsun.misc.Unsafe
, but a large gain in safety and maintainability. In particular, the use of standard APIs is guaranteed to work reliably on all platforms and all JDK releases, even if the JVM's implementation of arrays changes in the future.