-
JEP
-
Resolution: Unresolved
-
P2
-
None
-
None
-
None
-
Ron Pressler, Alex Buckley, & Mark Reinhold
-
Informational
-
Open
-
SE
Summary
Developers expect that their code and data is protected against use that is unwanted or unwise. The Java Platform, however, contains unsafe APIs that can undermine this expectation, thereby damaging the correctness, maintainability, scalability, security, and performance of applications. Going forward, we will restrict the unsafe APIs so that, by default, libraries, frameworks, and tools cannot use them. Application authors will have the ability to override this default.
What is integrity?
The Oxford English Dictionary defines “integrity” as “the state of being whole and undivided; the condition of being sound in construction.”
In the context of a computer program, integrity means that the
constructs from which we build the program — and ultimately the program
itself — are both whole and sound. Such constructs, whether they are
low-level language facilities such as for
loops or higher-level
components such as classes or modules, have both specifications and
implementations. Integrity thus requires two things of a computing
construct:
Its specification must say everything that needs to be said in order to make effective use of the construct (wholeness), and
Its implementation must satisfy its specification (soundness).
In more familiar terms, we say that a computing construct has integrity if, and only if, its specification is complete and its implementation is correct with respect to the specification.
For example, the specification of Java arrays says that an array can only be accessed within the bounds set for it upon creation. This constraint is guaranteed by the JVM, which raises an exception if it is violated.
The specification of Java arrays contains many other statements; e.g., that the length of an array never changes, that the first element of an array always has the index zero, and that accessing an array element after setting that element to some value returns exactly the same value (modulo concurrency). The JVM guarantees all of these statements — hence arrays are correct. Taken together, moreover, these statements capture all that we need to know in order to reason about any particular use of arrays — hence arrays are complete. We do not need to wonder, e.g., whether an array might silently increment all its elements at midnight on alternate Wednesdays, because its specification says nothing about midnight, or Wednesdays, and in fact its specification implies that this absurd situation cannot happen. Thus we can say that Java arrays have integrity.
(Integrity has practical limits, of course; the JVM cannot prevent native code or external debuggers or cosmic rays from modifying array content. When we speak of integrity here, we mean integrity within the context of the Java Platform.)
The Java Platform contains not just arrays but many other useful constructs, in both the language and in its built-in libraries. All of these constructs have both specifications and implementations, which taken together give the Platform itself a specification and an implementation. We intend, naturally, that the overall Platform have integrity: Its specification says all that needs to be said in order to reason effectively about its use (completeness), and its implementation behaves according to its specification (correctness). The integrity of the Platform enables us to reason about the correctness of our own code, starting from the specifications of the Platform's constructs.
Benefits of integrity
The Java Platform's integrity underpins many of its key benefits.
The Platform specifies that variables, fields, and arrays are initialized before use, thus a program's initial state is well defined.
The Platform specifies automatic storage management, thus a program never suffers from use-after-free errors.
The Java language and the Java Virtual Machine are specified so as to guarantee
, thus a program cannot perform invalid operations on data, such as treating aString
as aSocket
.The Platform API (as of Java 20) does not allow threads to be stopped arbitrarily, thus a multi-threaded program never sees objects in an inconsistent state.
Without integrity, we cannot rely upon any of these valuable properties.
Integrity via encapsulation
The Java language provides built-in constructs which enable us to build our own constructs at higher and higher levels of abstraction, by hiding unnecessary detail. We compose statements into methods, methods and fields into classes, classes into packages, packages into modules, and finally modules into entire programs.
Abstraction enables us to control program complexity: We can show that the implementation of a higher-level construct meets its specification by reasoning solely from the specifications of the lower-level constructs upon which it is built; there is no need to consider the implementation details of the lower-level constructs, nor the specifications of any other constructs. Likewise, users of the higher-level construct need only refer to the specifications of that construct, and of any other constructs that they use, when reasoning about their own code; there is no need to consider the implementation details of the higher-level construct, nor the specifications of any other constructs. Ultimately, we can, in principle, show that an entire program meets its specification via such reasoning.
For all of this to work requires that our higher-level constructs themselves have integrity: They must be complete and correct. A key tool for achieving that is encapsulation.
For example, suppose we want to build a counter abstraction that is
always even, never odd. Imagine that the Java language had no
encapsulation, so that all fields and methods could be accessed from
anywhere, as if everything were public
. We might declare an
EvenCounter
class, like so:
/**
* Specification:
* - value() initially returns zero
* - incrementByTwo() increments value() by two
* - decrementByTwo() decrements value() by two
* - value() is always even, never odd
*/
/*public*/ final class EvenCounter {
/*public*/ int x = 0;
/*public*/ int value() { return x; }
/*public*/ void incrementByTwo() { x += 2; }
/*public*/ void decrementByTwo() { x -= 2; }
}
We can easily show that the EvenCounter
class, in isolation, meets its
specification, thus it is correct. Its specification, however, is not
complete: It does not say everything that needs to be said in order to
make effective use of the class. That is because code external to the
class can set the x
field to an odd number at any time, thereby
causing the value()
method to violate the class's specification. To
show that a use of the class is correct we must analyze every line of
the entire program to ensure that no code external to the class modifies
this field. Rather than simple local reasoning about each such use,
complex global reasoning is required. It is as if the specification of
the EvenCounter
class includes the additional requirement that
* - No code external to this class modifies the x field
With the actual Java language, of course, there is no need for this
complexity since the language provides encapsulation constructs — the
private
and public
keywords — which allow us to protect data from
intentional or unintentional modification.
public final class EvenCounter {
private int x = 0;
public int value() { return x; }
public void incrementByTwo() { x += 2; }
public void decrementByTwo() { x -= 2; }
}
Here we use the private
keyword to protect the x
field from external
access. The private
keyword has integrity: Its specification says
that a private field can be modified only by code in the same class, and
the Java compiler and the JVM guarantee this specification throughout
the program. Making the x
field private thus obviates the need to
analyze the entire program when reasoning about the correctness of any
use of the EvenCounter
class. In other words, local reasoning about
each such use is sufficient. The class's original specification, above,
is thus complete; we already know that the class is correct with respect
to that specification, thus the class has integrity.
Abstraction enables us to create higher-level computing constructs; encapsulation enables us to imbue those constructs, and ultimately entire programs, with integrity. This provides tremendous value.
Correctness — The correctness of a program can rest upon the integrity of the
EvenCounter
class, in particular the fact that the value in an instance is always even. An application could, e.g., useEvenCounter
to track business activity where every purchase needs to match a sale, resulting in an even number of transactions. Using encapsulation to imbue the class with integrity ensures that correctness cannot be undermined by code external to the class.Maintainability — Encapsulation protects code as it evolves. We assume that
private
fields and methods are implementation details, able to be safely changed without breaking clients. The private field of theEvenCounter
class cannot be accessed by client code, thus we can change it at will so long as we preserve correctness. We could, e.g., rename the field, or change its type toInteger
. Encapsulation gives classes the integrity required to enable independent internal evolution.Scalability — Encapsulation is a cornerstone of programming in the large because it ensures the integrity that enables local reasoning about the behavior of computing constructs. Programs can be built from independently-developed components that interact only through their public APIs and behave according to their specifications. This allows not just individual programs but the entire Java ecosystem to scale as collections of independent interoperating components.
Security — Encapsulation is essential for any kind of robust security. Suppose, e.g., that a class in the JDK restricts a sensitive operation:
if (isAuthorized()) doSensitiveOperation();
The restriction is robust only if we can guarantee that
doSensitiveOperation()
is only ever invoked after a successfulisAuthorized()
check. If we declaredoSensitiveOperation()
asprivate
in its declaring class then we know that no code in any other class can directly invoke that method; in other words, the declaring class has integrity with respect to that method. Code reviewers thus need only ensure that all invocations of the method within the declaring class are preceded by anisAuthorized()
check; they can ignore all the other code in the program.Performance — In the Java runtime, numerous optimizations can benefit from the integrity ensured by encapsulation. The JVM can, e.g., perform
optimizations when it determines that the value of aprivate
field never changes. Going further, a tool such asjlink
could remove unusedprivate
methods at link time to reduce image size and class loading time.
Undermining integrity
Encapsulation is a key tool for establishing integrity. It underpins correctness, maintainability, scalability, security, and performance. There are, however, four APIs in the JDK which can circumvent it.
The <code class="prettyprint" data-shared-secret="1735220887281-0.3568594305775814">AccessibleObject::setAccessible(boolean)</code> method in the <code class="prettyprint" data-shared-secret="1735220887281-0.3568594305775814">java.lang.reflect</code> package enables deep reflection, which is reflection over fields and methods without regard to encapsulation boundaries. This method was introduced in Java 1.2 to support object serialization and deserialization, but in practice any code can use it to invoke the
private
methods of any class, read and write theprivate
fields of any object, and even writefinal
fields.The
sun.misc.Unsafe
class includes methods that can accessprivate
methods and fields, and writefinal
fields, similar to deep reflection.The Java Native Interface (JNI) allows native code to interact with Java objects without regard to encapsulation boundaries. Native code can access
private
methods and fields, and writefinal
fields, similar to deep reflection.The Instrumentation API allows components called agents to modify the bytecode of any method in any class. An agent can, e.g., rewrite the
incrementByTwo
method of theEvenCounter
class to log the old value ofx
to a file before incrementing it.
We refer to these APIs as unsafe because they violate the integrity of
the Java language's encapsulation constructs, thereby violating the
integrity not only of the Platform itself but of every component and
program built on top of it. The private
field in an EvenCounter
object could, e.g., be modified from outside the class via deep
reflection, sun.misc.Unsafe
, or native code, resulting in an odd
value, violating the class's specification. The public
methods of
EvenCounter
could be redefined by an agent to increment the private
field by one instead of two, again resulting in an odd value.
The fact that the language's encapsulation constructs lack integrity
destroys the ability to reason locally about a program's correctness.
To show that a use of an encapsulated component is correct we must
analyze every class on the class path, on the module path, or loaded
dynamically, and either rule out the use of unsafe APIs or else ensure
that their use does not violate the component's specification. This
analysis is not practical, thus any code that relies on the evenness of
EvenCounter
objects for its own correctness may behave incorrectly,
and any client of that code may behave incorrectly, and so on.
Even if a library uses an unsafe API with good intentions, and does not
explicitly violate any other component's specification, it could still
enable specification violations in an application that uses it. A JSON
serialization library could, e.g., deserialize an EvenCounter
object
by using deep reflection to set the value of the object's private field,
bypassing EvenCounter
's public API. This, in itself, does not violate
the specification of the EvenCounter
class. If the application does
not, however, take care to explicitly validate that its JSON input does
not contain an odd number, then reading such input will result in an odd
EvenCounter
. The serialization library does not explicitly violate
EvenCounter
's specification, but by circumventing EvenCounter
's
defense mechanism — its encapsulation — it makes it vulnerable to
indirect specification violations.
This problem is especially serious with security-sensitive components. A vulnerability in a library that uses an unsafe API jeopardizes the integrity of every component of the application, and could allow an adversary to manipulate input to the application in a way that undermines security.
The unsafe APIs in the JDK violate the integrity of language constructs other than those related to encapsulation. Constructs that access arrays and objects, in particular, are specified so as to ensure
: An array cannot be accessed beyond its bounds, and an object cannot be accessed after its storage is reclaimed. We have relied on the memory safety of the Java Platform for decades, but it can be violated by the unsafe APIs, leading to and even JVM crashes.JNI allows the execution of native code that can violate memory safety. Native code can also produce a
that wraps arbitrary memory locations, which means any Java code that can cause undefined behavior.The Foreign Function & Memory API (FFM,
) allows the execution of native code that can violate memory safety. The FFM API also allows Java code to create a that wraps arbitrary memory locations. Any Java code that accesses such a segment can cause undefined behavior.The
sun.misc.Unsafe
class includes methods that can read and write arbitrary memory locations, both on and off the JVM's heap. Thus an array can be accessed beyond its bounds, and an object's storage can be accessed long after it is reclaimed by the garbage collector — a classic use-after-free error.
The integrity of the Java Platform — and hence the correctness, maintainability, scalability, security, and performance of our programs — requires that we prevent encapsulation from being circumvented and memory safety from being violated. How can we square this with the presence of the unsafe APIs, which are designed to offer library, framework, and tool developers special superpowers for use in rare situations in which there is no other way to solve a problem? The answer is that we must adopt integrity by default.
Integrity by default
Integrity by default means that every construct of the Java Platform has integrity, unless overridden explicitly at the highest level of the program. That is, the developer of an application can choose to give up selected kinds of integrity within the scope of that application; the developer of a library, framework, or tool, however, cannot. An application developer can, e.g., choose to configure the Java runtime to allow a serialization library to use unsafe APIs, knowingly acquiescing to a loss of integrity because the library's functionality is indispensable. Without such explicit permission, however, that library cannot, on its own, violate any aspect of Platform or application integrity.
We have gradually been moving the Java Platform toward integrity by default since JDK 9. We have done so by selectively degrading or gating the ability of the unsafe APIs to undermine integrity. This effort has three strands.
JDK code is strongly encapsulated in modules. By default, deep reflection cannot circumvent strong encapsulation.
Unsafe APIs that are standard in the Java Platform are restricted. By default, Java code cannot circumvent encapsulation by using the Instrumentation API to redefine methods, or violate encapsulation or memory safety by using JNI or FFM to call native code.
Unsafe APIs that are non-standard are removed when standard replacement APIs become available. The replacement APIs are designed so that, by default, they cannot undermine integrity.
Strong encapsulation: The antidote to deep reflection
JDK 9 introduced modules to the Java language. A module is a set of packages designed to work together and intended for re-use. If a package is exported then its public elements can be used outside the module; if a package is not exported then its public elements can be used only inside the module.
Modules provide strong encapsulation, which means that reflection by
code outside of a module cannot access the private elements of any class
within the module. That is, the setAccessible
method respects module
boundaries. If the public EvenCounter
class, e.g., is declared in an
explicit module, then its private field x
cannot be modified by deep
reflection initiated by code outside the module.
Restrictions on standard unsafe APIs
Most of the unsafe APIs — setAccessible
, JNI, FFM, and Instrumentation
— continue to be supported in the Java Platform. While they are rarely
used by application code directly, they are essential for a relatively
small number of libraries, frameworks, and tools whose core
functionality cannot be implemented any other way. Examples include:
Frameworks for unit testing and dependency injection (DI) that use deep reflection to access
private
fields and methods of application classes;Serialization libraries that use deep reflection to access
private
fields of application classes;Mocking libraries that use the Instrumentation API to redefine methods of application classes;
Native wrapper libraries that use JNI to call
native
methods or FFM to invoke downcall method handles; andApplication Performance Monitoring (APM) tools that use agents to inject logging and performance counters into application code.
A component that uses an unsafe API violates the integrity of the Java Platform: It introduces the possibility that encapsulation will be circumvented or memory safety will be violated, thereby rendering the specification of the Platform incomplete. If the Platform has no integrity then components built on top of it have no integrity, and applications themselves have no integrity. The policy of integrity by default enshrines the idea that the developer of a library, framework, or tool cannot unilaterally decide to violate integrity by using an unsafe API. That power — and the corresponding responsibility — belongs solely to the application's developer (or perhaps deployer, on the advice of the developer). The application's developer answers to end users for the behavior of the application; developers of libraries, frameworks, and tools, by contrast, do not.
We cannot treat the mere inclusion of an unsafe-using library or framework in an application as consent by the application's developer to violate integrity. The developer might not be aware that the component uses an unsafe API. The developer might not even be aware that the component is present, since the component could be an indirect dependency several layers removed from the application itself. The application developer must therefore explicitly configure the Java runtime to allow selected components to use unsafe APIs. If the runtime is not suitably configured then using an unsafe API causes an exception to be thrown. In other words, use of unsafe APIs is restricted by default.
Various command-line options configure the Java runtime to allow the use of unsafe APIs:
<code class="prettyprint" data-shared-secret="1735220887281-0.3568594305775814">--add-opens</code> allows code in specified modules to use
setAccessible
on theprivate
elements of other modules. A related option,--add-exports
, allows code in specified modules to accesspublic
elements of otherwise unexported packages.<code class="prettyprint" data-shared-secret="1735220887281-0.3568594305775814">--enable-native-access</code> allows code in specified modules to use FFM to create arbitrary memory segments and to find and invoke native code. In the future, this option will also be required to enable the use of JNI.
<code class="prettyprint" data-shared-secret="1735220887281-0.3568594305775814">-javaagent</code> allows an agent to use the Instrumentation API. A related option,
-XX:+EnableDynamicAgentLoading
, allows tools to load agents dynamically.
Application developers can specify these options in multiple ways:
Pass them directly to the
java
launcher in the script that starts the application,Pass them indirectly to the
java
launcher by setting the environment variable <code class="prettyprint" data-shared-secret="1735220887281-0.3568594305775814">JDK_JAVA_OPTIONS</code>,Place them in an argument file that is passed to the
java
launcher (e.g.,java @config
) by a script or an end user,Place corresponding manifest entries in the application's executable JAR file (
Add-Opens
,Add-Exports
,Enable-Native-Access
, andLauncher-Agent-Class
; there is no manifest entry corresponding to-agentlib
)Configure the runtime programmatically as described in Embracing integrity by default.
No matter how they are specified, these options configure the Java runtime when it starts, enabling the JVM to determine how integrity will be undermined and which optimizations should be enabled or disabled. These options also make it easy for application developers to audit the use of unsafe APIs and understand the risks posed to correctness, maintainability, scalability, security, and performance. If none of these options is used then the application developer can be certain that neither the application nor its dependencies violate the integrity of the Platform, the application's dependencies, or the application itself.
Adapting to restrictions on unsafe APIs
Most of the unsafe APIs in the Java Platform were in use for years before they were deemed unsafe, so it is not practical to restrict them without notice: applications would fail unexpectedly. In addition, configuring the Java runtime to allow the use of unsafe APIs by libraries, frameworks, and tools is not part of the traditional developer experience. To alert application developers to the need to configure the Java runtime, we restrict the use of a pre-existing unsafe API in a gradual fashion:
- In an initial JDK release, the API can be used as normal.
- In a later JDK release, the API can be used, but doing so produces a warning. The warning identifies the library that used the API and frames the use as "illegal". The warning also explains how to configure the Java runtime to allow the use, e.g., with
--add-opens
. Only the first use of the API by a particular module causes a warning; further use by code in the same module does not cause further warnings. - Eventually, in another JDK release, the API cannot be used by default. Calling the API causes an exception to be thrown, unless the Java runtime has been configured to allow the use.
This process typically takes a few years, during which time the JDK offers a temporary command-line option that lets the application developer "dial up" or "dial down" the process. For example, --add-opens
had a temporary counterpart of <code class="prettyprint" data-shared-secret="1735220887281-0.3568594305775814">--illegal-access</code>. The temporary option has three settings:
allow
(orpermit
) -- allow use of the API, with no warnings.warn
-- allow use of the API, with warnings.deny
-- disallow use of the API, and throw an exception.
Typically, the temporary option defaults to allow
in the initial JDK release, then warn
in a later JDK release, and eventually deny
. Application developers can "dial up" the option to deny
at any time, simulating the long-term behavior planned for the Java runtime. In contrast, the ability to "dial down" the option is limited: when the default is warn
, the option can be set to allow
, but once the default is deny
, the option can only be set to warn
, not allow
. After the default has been deny
for some time, the temporary option is removed.
Removing non-standard unsafe APIs
The sun.misc.Unsafe
class includes methods that perform a variety of
low-level operations without any safety checks. Since JDK 9 we
have been adding standard APIs that offer safer replacements for this
functionality. The low-level manipulation of objects in the JVM's heap,
e.g., can now be done more safely via the
<code class="prettyprint" data-shared-secret="1735220887281-0.3568594305775814">VarHandle</code>
API, and manipulation of data in off-heap memory can now be done more
safely via FFM's
<code class="prettyprint" data-shared-secret="1735220887281-0.3568594305775814">MemorySegment</code>
API.
We have already deprecated for removal and, later, removed some elements
of sun.misc.Unsafe
which now have standard API replacements. We will
continue to do so in future releases. Ultimately, we will deprecate
sun.misc.Unsafe
itself for removal, and then remove it.
Embracing integrity by default
Libraries, frameworks, and tools can relieve application developers of some of the effort of configuring the Java runtime in many situations.
Developers of dependency injection frameworks can ask application developers to grant access to the application's
private
fields and methods directly in the code. One approach is to ask application developers to open the packages of their modules to the framework module by placing, e.g.,opens com.example.app to org.framework;
in their module declarations. Deep reflection can access every element in an open package, even
private
elements. A framework can, if necessary, transfer its access rights to another component via <code class="prettyprint" data-shared-secret="1735220887281-0.3568594305775814">Module::addOpens</code>.A better approach is to ask application developers to create
method-handle lookup<br /> objects
and pass them to the framework; e.g.,Framework.grantAccess(MethodHandles.lookup());
A lookup object grants access to the
private
elements accessible to the code that created it, so the framework can use the lookup object to perform deep reflection on application code without any application packages being open.Serialization libraries have caused many security vulnerabilities by using deep reflection to access private fields of application classes. In general, it is a mistake for libraries to serialize and deserialize an object without the cooperation of the object's class. Objects such as strings, records, enums, and collections are easy to serialize and deserialize because their classes provide
public
accessors and constructors. For other objects, serialization libraries should specify protocols by which classes can expose their state during serialization and accept and validate new state during deserialization. For this to work, application developers may need to grant access to classes in non-exported packages by opening packages or passing lookup objects.Some classes already take responsibility for their own serialization and deserialization by implementing the <code class="prettyprint" data-shared-secret="1735220887281-0.3568594305775814">java.io.Serializable</code> interface. Serialization libraries can take advantage of that by invoking the
writeObject
andreadObject
methods of such classes via the <code class="prettyprint" data-shared-secret="1735220887281-0.3568594305775814">sun.reflect.ReflectionFactory</code> class, which issupported for this<br /> purpose
.In the long term, we expect the Java Platform to offer
.Unit-testing frameworks and mocking libraries can integrate with build tools such as Maven and Gradle to configure the Java runtime automatically. Build tools could, e.g., start test runs with options necessary to circumvent encapsulation
(--add-opens
,--add-exports
), patch modules (--patch-module
), and install agents (-javaagent
).
More elaborate frameworks and applications that wish to control the initialization and bootstrapping of the runtime and/or of components can programmatically grant code permission to use unsafe APIs:
An application with a custom native launcher that loads the JVM through the JNI invocation API can programmatically pass the JVM options such as
--add-opens
,--enable-native-access
, or-javaagent
.A framework or an application that dynamically loads component modules can allow the component to use unsafe APIs with the
addExports
,addOpens
, andenableNativeAccess
methods of the <code class="prettyprint" data-shared-secret="1735220887281-0.3568594305775814">ModuleLayer.Controller</code> API.
Integrity beyond the Java Platform
Java code can use standard facilities of the Platform to reach outside the Java runtime and violate integrity. Java code can, e.g., alter the content of a class file in the file system before the class is loaded. However, a good principle in matters of integrity is that
The integrity of components is best enforced by the infrastructure that provides them.
The integrity of the file system and its content is the responsibility of the operating system, not the Java runtime. The OS or, if appropriate, an OS-level container, should always be configured so as to protect the integrity of the Java runtime's files and memory, and the integrity of the application’s files, regardless of the measures taken by the Java runtime to protect its own integrity and that of the application it is running.
Why now?
The Java ecosystem has managed just fine without strong encapsulation or restrictions on unsafe APIs for nearly three decades. Why are we now adopting integrity by default, which adds overhead for some library, framework, tool, and application developers?
The answer is that, in recent years, both the JDK and the environment in which Java applications run have changed.
Correctness — Historically, the Java runtime has been able to ensure the integrity of many low-level constructs because they were implemented in native code, beyond the reach of the unsafe APIs. However, more and more of the Java runtime itself is being written or rewritten in Java. This means that more and more of the Platform's integrity depends upon the integrity of Java code, which can be violated by the unsafe APIs.
Maintainability — In order to add new features without drowning in maintenance, we need to be able to remove obsolete components from the JDK and refactor its implementation at will. Unfortunately, over time various libraries, frameworks, and tools came to depend on some of the JDK's internal APIs, which they assumed were stable. As a result, it was increasingly difficult for the ecosystem to migrate to newer releases. We could either accept a slowing pace of Platform evolution or inflict migration pain just once more, in JDK 17, by strongly encapsulating the JDK's internal APIs. (We modularized the JDK in JDK 9, but we only fully enabled strong encapsulation in JDK 17.)
Security — With the impending removal of the Security Manager (JEP 411), we need strong encapsulation to support the creation of robust security layers protected from interference by other code, as shown earlier. Without strong encapsulation, any vulnerable code in the application could compromise security.
Performance — There is growing demand to improve startup time, warmup time, and image size, which are important for deploying Java applications in modern cloud environments. Some techniques for achieving these goals require that classes do not change over time by, e.g., being redefined via the Instrumentation API. Other important optimizations, such as constant folding, require that constructs such as
final
fields have integrity, so that their values are actually final and cannot be modified.
In short: The use of JDK-internal APIs caused serious migration issues, there was no practical mechanism that enabled robust security in the current landscape, and new requirements could not be met. Despite the value that the unsafe APIs offer to libraries, frameworks, and tools, the ongoing lack of integrity is untenable. Strong encapsulation and the restriction of the unsafe APIs — by default — are the solution.
- relates to
-
JDK-8061972 JEP 261: Module System
- Closed
-
JDK-8132928 JEP 260: Encapsulate Most Internal APIs
- Closed
-
JDK-8255363 JEP 396: Strongly Encapsulate JDK Internals by Default
- Closed
-
JDK-8263547 JEP 403: Strongly Encapsulate JDK Internals
- Closed
-
JDK-8306275 JEP 451: Prepare to Disallow the Dynamic Loading of Agents
- Closed
-
JDK-8323072 JEP 471: Deprecate the Memory-Access Methods in sun.misc.Unsafe for Removal
- Closed
-
JDK-8307341 JEP 472: Prepare to Restrict the Use of JNI
- Completed