Exploring Java 8 Profiles
by Ben Evans
Published May 2014
What can Compact Profiles do for your application?
Java 8 introduces the concept of Compact Profiles, which are reduced versions of the Java runtime environment (JRE) that do not contain the usual full contents of rt.jar. In this article, we will explore the advantages of using Compact Profiles and how they point the way toward a modular future for the JDK.
Originally published in the May/June 2014 issue of Java Magazine. Subscribe today.
The current versions of the JRE are quite monolithic: rt.jar is at least 60 MB on its own, without taking into account the size of native code loaded as dynamic libraries. If we can reduce the size of the Java platform footprint and move to a modular view of the JDK, we can realize some great benefits:
- Faster Java Virtual Machine (JVM) startup times
- Reduced resource consumption
- Removal of packages that, in hindsight, shouldn’t be in the core
- Improved security, because removing unused classes reduces the attack surface of the platform
- Convergence of the Java ME Connected Device Configuration (CDC) with Java SE
The initial approach to this effort was a full modularization of the platform, known as Project Jigsaw. This ambitious project also included other goals:
- Incorporate best practices for dependencies into the platform core, and apply lessons learned about dependency management from tools such as Maven, Apache Ivy, OSGi standard, and Linux distributions.
- Isolate dependencies—solve the library versioning problem.
- Allow application developers to package their code as modules, rather than as JAR files.
The catch is that in order to achieve the full set of goals, major surgery on the Java platform core is required. In particular, a new approach to classloading—involving a “modular classloader”—is required. This has a lot of edge cases and is a complex undertaking, especially if we need to maintain backward compatibility.
In order to maintain the release date for Java 8, a decision was made to move full modularity out to Java 9, targeted for 2016. Java 8 Compact Profiles are designed to be a first step toward full modularity, with some of the basic advantages noted above. They build on the initial work done for modularity and provide a cut-down Java runtime, which
- Is fully compliant with the JVM and Java language specifications
- Has a substantially reduced footprint
- Removes functionality that is not always needed (for example, CORBA)
- Works for many applications (especially server- side code)
Compact Profiles are based on packages; they contain a number of full packages, and no partial packages are currently allowed. They are also subject to two other restrictions:
- A profile must form a closed set; references to classes not contained in the profile are not allowed.
- If a profile contains some classes from another, smaller profile, it must contain all of them, so partially overlapping profiles are not allowed. Put another way, profiles are additive.
The smallest Compact Profile is called
compact1, and it comprises the following packages:
Two other Compact Profiles are currently specified:
compact2, which adds packages used for remote method invocation (RMI), SQL, and XML, and
compact3, which comprises all of
compact2 plus tooling and management packages (including Java Management Extensions [JMX]) as well as additional cryptography libraries. The smallest profile,
compact1, occupies around 11 MB, which is a significant space savings.
Note that as currently specified, all of these profiles are headless; they do not contain any GUI classes. Any applications requiring GUI support (Swing or AWT) must use a full JRE.
Get SmallerJava 8 introduces the concept of Compact Profiles, which are reduced versions of the Java runtime environment (JRE) that do not contain the usual full contents of rt.jar.
To make use of Compact Profiles, developers require tools. One important question is whether an application can run against a specific profile. Java 8 ships with two tools that have been enhanced to help answer this question: both
jdeps have been modified to be aware of profiles.
javac is the tool of choice for determining whether a collection of source code can be safely run on a specific profile. This is achieved by using the new
javac -profile <profile> will cause a compilation error to be generated for any usage of a class not present in the indicated profile.
In some cases, however, source code is not available or a recompilation run is inconvenient. Fortunately, in this case, the new
jdeps tool can help.
jdeps is a new static analysis tool that ships with Java 8. It provides an analysis of the dependencies that a specific class or JAR file has. This tool is extremely useful (and not just for profile dependencies) and features a -
-P) switch that indicates which packages depend on which profiles.
Let’s take a look at an example, which summarizes the package dependencies for the popular JUnit testing library. See Listing 1, which shows good news; everyone should be able to test their code.
jdeps -s -P junit.jar junit.jar -> compact1
If we want more information, we can use the
-v switch for verbose output, which will give us a lot of detail about each class inside the JAR file. See Listing 2 (some of the output was truncated because it was 2,152 lines long).
jdeps -v junit.jar junit.jar -> /Library/Java/JavaVirtualMachines/openjdk8/Contents/ Home/jre/lib/rt.jar junit.extensions.ActiveTestSuite (junit.jar) -> java.lang.Class -> java.lang.InterruptedException -> java.lang.Object -> java.lang.String -> java.lang.Thread -> junit.extensions.ActiveTestSuite$1 junit.jar -> junit.framework.Test junit.jar -> junit.framework.TestCase junit.jar
If we want a slightly more high-level view, we can use
-V package to show dependencies between packages, as shown in Listing 3 (some of the output was truncated because it was 1,297 lines long).
jdeps -V package junit.jar junit.jar -> /Library/Java/JavaVirtualMachines/openjdk8/Contents/Home/jre/lib/rt.jar junit.extensions (junit.jar) -> java.lang junit.framework (junit.jar) -> java.io -> java.lang -> java.lang.annotation -> java.lang.reflect -> java.util junit.runner (junit.jar) -> java.io -> java.lang -> java.lang.reflect -> java.text -> java.util
jdeps is also very flexible about what it will accept as input: a JAR file, a directory, or a single
.class file. It provides capabilities for recursive traversal and for specifying that only packages with a name that matches a given regular expression should be considered. It can also warn that code uses an internal API and is not portable between Java environments (and might break if run against a future version of Java).
Finally, let’s look at the NetBeans IDE. The current version is 8.0, which already has support for a wide range of JDK 8 features, including Compact Profiles. When selecting which JDK or JRE to use in Project Properties, for JDK 8 and later, a developer can choose whether to compile against the full JRE or a profile. This makes it much easier to ensure that when targeting a particular profile, unwanted dependencies don’t creep in. With luck, other IDEs will follow suit and also add support to allow developers to write code in the IDE that checks conformance with a specific profile at development time.
You ChooseA developer can choose whether to compile against the full JRE or a profile.
A Word About Stripped Implementations
In addition to Compact Profiles, there is another proposed new technique for reducing resource utilization for deployed JVM applications: Stripped Implementations. A Stripped Imple-men-tation is a reduced JRE, which is packaged with an application that has the exclusive use of it. Because the application is the only possible client for the Stripped Implementation, the runtime can be aggressively pruned, removing packages, classes, and even methods that are not used by the application.
This approach is advantageous in circumstances where resource limitations are severe. It relies on extensive testing to ensure that nothing that the application could rely on is removed by the stripping process. In general, it is extremely difficult to get a precise accounting of an application’s dependencies. This is due in large part to the existence of techniques such as reflection and classloading, which can greatly complicate (or even render impossible) the task of ascertaining the true dependencies of a set of classes.
Compact Profiles are very different from Stripped Implementations—the former are Java runtimes designed for general-purpose use and are complete implementations of the Java language specification, whereas Stripped Implementations are defined to be single-use, nonreusable implementations that do not have to conform to the Java language specification at their point of delivery to the user.
Stripped Implementations were targeted as a feature for Java SE 8, but due to some complications with licensing and the current testing kit, they had to be dropped very close to the release date. Nevertheless, the intention is for Stripped Implementations to become part of the platform shortly after the release of Java 8, and when combined with Compact Profiles, they have the potential to provide extremely small footprints for single-use applications.
Java 8 Compact Profiles represent a significant step toward future goals for the platform—both in terms of embedded (or capability-restricted) development and also for server-side developers. While not being the complete modularity solution we might have wished for in Java 8, Compact Profiles are a useful development in their own right.