Chapter 13. Platform Tools

This chapter discusses the tools that ship with the OpenJDK version of the Java platform. The tools covered are all command-line tools. If you are using a different version of Java, you may find similar but different tools as part of your distribution instead.

Later in the chapter, we devote a dedicated section to the jshell tool, which has introduced interactive development to the Java platform as of version 9.

Command-Line Tools

The command-line tools we cover are the most commonly used tools, and those of greatest utility—they are not a complete description of every tool that is available. In particular, tools concerned with CORBA and the server portion of RMI are not covered, as these modules have been removed from the platform with the release of Java 11.

Note

In some cases, we need to discuss switches that take filesystem paths. As elsewhere in the book, we use Unix conventions for such cases.

The tools we are discussing are:

  • javac

  • java

  • jar

  • javadoc

  • jdeps

  • jps

  • jstat

  • jstatd

  • jinfo

  • jstack

  • jmap

  • javap

  • jaotc

  • jlink

  • jmod

javac

Basic usage

javac some/package/MyClass.java

Description

javac is the Java source code  compiler—it produces bytecode (in the form of .class files) from .java source files.

For modern Java projects, javac is not often used directly, as it is rather low-level and unwieldy, especially for larger codebases. Instead, modern integrated development environments (IDEs) either drive javac automatically for the developer or have built-in compilers for use while code is being written. For deployment, most projects will make use of a separate build tool, such as Maven, Ant, or Gradle. Discussion of these tools is outside the scope of this book.

Nevertheless, it is useful for developers to understand how to use javac, as there are cases when compiling small codebases by hand is preferable to having to install and manage a production-grade build tool such as Maven.

Common switches
-classpath

Supplies classes we need for compilation.

-d some/dir

Tells javac where to output class files.

@project.list

Load options and source files from the file project.list.

-help

Help on options.

-X

Help on nonstandard options.

-source <version>

Control the Java version that javac will accept.

-target <version>

Control the version of class files that javac will output.

-profile <profile>

Control the profile that javac will use when compiling the application. See later in this chapter for more detail on Compact Profiles.

-Xlint

Enable detail about warnings.

-Xstdout

Redirect output of compilation run to a file.

-g

Add debug information to class files.

Notes

javac has traditionally accepted switches (-source and -target) that control the version of the source language that the compiler would accept, and the version of the class file format that was used for the outputted class files.

This facility introduces additional compiler complexity (as multiple language syntaxes must be supported internally) for some small developer benefit. In Java 8, this capability has begun to be slightly tidied up and placed on a more formal basis.

From JDK 8 onward, javac will only accept source and target options from three versions back. That is, only the formats from JDK 5, 6, 7, and 8 will be accepted by javac. This does not affect the java interpreter—any class file from any Java version will still work on the JVM shipped with Java 8.

C and C++ developers may find that the -g switch is less helpful to them than it is in those other languages. This is largely due to the widespread use of IDEs in the Java ecosystem—integrated debugging is simply a lot more useful, and easier to use, than additional debug symbols in class files.

The use of the lint capability remains somewhat controversial among developers. Many Java developers produce code that triggers a large number of compilation warnings, which they then simply ignore. However, experience on larger codebases (especially on the JDK codebase itself) suggests that in a substantial percentage of cases, code that triggers warnings is code in which subtle bugs may lurk. Use of the lint feature, or static analysis tools (such as FindBugs), is strongly recommended.

java

Basic usage

java some.package.MyClass java -jar my-packaged.jar

Description

java is the executable that starts up a Java Virtual Machine. The initial entry point into the program is the main() method that exists on the named class, and that has the signature:

public static void main(String[] args);

This method is run on the single application thread that is created by the JVM startup. The JVM process will exit once this method returns (and any additional nondaemon application threads that were started have terminated).

If the form takes a JAR file rather than a class (the executable JAR form), the JAR file must contain a piece of metadata that tells the JVM which class to start from.

This bit of metadata is the Main-Class: attribute, and it is contained in the MANIFEST.MF file in the META-INF/ directory. See the description of the jar tool for more details.

Common switches
-cp <classpath>

Define the classpath to read from.

-X, -?, -help

Provide help about the java executable and its switches.

-D<property=value>

Sets a Java system property that can be retrieved by the Java program. Any number of such properties can be specified this way.

-jar

Run an executable JAR (see the entry for jar).

-Xbootclasspath(/a or /p)

Run with an alternative system classpath (very rarely used).

-client, -server

Select a HotSpot JIT compiler (see “Notes” for this entry).

-Xint, -Xcomp, -Xmixed

Control JIT compilation (very rarely used).

-Xms<size>

Set the minimum committed heap size for the JVM.

-Xmx<size>

Set the maximum committed heap size for the JVM.

-agentlib:<agent>, -agentpath:<path to agent>

Specify a JVM Tooling Interface (JVMTI) agent to attach to the process being started. Agents are typically used for instrumentation or monitoring.

-verbose

Generate additional output, sometimes useful for debugging.

Notes

The HotSpot VM contains two separate JIT compilers—known as the client (or C1) compiler and the server (or C2) compiler. These were designed for different purposes, with the client compiler offering more predictable performance and quicker startup, at the expense of not performing aggressive code optimization.

Traditionally, the JIT compiler that a Java process used was chosen at process startup via the -client or -server switch. However, as hardware advances have made compilation ever cheaper, a new possibility has become available—to use the client compiler early on, while the Java process is warming up, and then to switch to the high-performance optimizations available in the server compiler when they are available. This scheme is called Tiered Compilation, and it is the default in Java 8. Most processes will no longer need explicit -client or -server switches.

On the Windows platform, a slightly different version of the java executable is often used—javaw. This version starts up a Java Virtual Machine, without forcing a Windows console window to appear.

In older Java versions, a number of different legacy interpreters and virtual machine modes were supported. These have now mostly been removed, and any remaining should be regarded as vestigial.

Switches that start with -X were intended to be nonstandard switches. However, the trend has been to standardize a number of these switches (particularly -Xms and -Xmx). In parallel, Java versions have introduced an increasing number of -XX: switches. These were intended to be experimental and not for production use. However, as the implementations have stabilized, some of these switches are now suitable for some advanced users (even in production deployments).

In general, a full discussion of switches is outside the scope of this book. Configuration of the JVM for production use is a specialist subject, and developers are urged to take care, especially when modifying any switches related to the garbage collection subsystem.

jar

Basic usage

jar cvf my.jar someDir/

Description

The JAR utility is used to create and manipulate Java Archive (.jar) files. These are ZIP format files that contain Java classes, additional resources, and (usually) metadata. The tool has five major modes of operation—Create, Update, Index, List, and Extract—on a JAR file.

These are controlled by passing a command option character (not a switch) to jar. Only one command character can be specified, but optional modifier characters can also be used.

Command options
  • c: Create a new archive

  • u: Update archive

  • i: Index an archive

  • t: List an archive

  • x: Extract an archive

Modifiers
  • v: Verbose mode

  • f: Operate on a named file, rather than standard input

  • 0: Store, but do not compress, files added to the archive

  • m: Add the contents of the specified file to the jar metadata manifest

  • e: Make this jar executable, with the specified class as the entry point

Notes

The syntax of the jar command is intentionally very similar to that of the Unix tar command. This similarity is the reason why jar uses command options, rather than switches (as the other Java platform commands do).

When you create a JAR file, the jar tool will automatically add a directory called META-INF that contains a file called MANIFEST.MF—this is metadata in the form of headers paired with values. By default, MANIFEST.MF contains just two headers:

Manifest-Version: 1.0
Created-By: 1.8.0 (Oracle Corporation)

Using the m option allows additional metadata to be added into MANIFEST.MF at JAR creation time. One frequently added piece is the Main-Class: attribute, which indicates the entry point into the application contained in the JAR. A JAR with a specified Main-Class: can be directly executed by the JVM, via java -jar, or double-clicking the JAR in a graphical file browser.

The addition of the Main-Class: attribute is so common that jar has the e option to create it directly in MANIFEST.MF, rather than having to create a separate text file for this purpose.

javadoc

Basic usage

javadoc some.package

Description

javadoc produces documentation from Java source files. It does so by reading a special comment format (known as Javadoc comments) and parsing it into a standard documentation format, which can then be output into a variety of document formats (although HTML is by far the most common).

For a full description of Javadoc syntax, refer to Chapter 7.

Common switches
-cp <classpath>

Define the classpath to use

-D <directory>

Tell javadoc where to output the generated docs

-quiet

Suppress output except for errors and warnings

Notes

The platform API docs are all written in Javadoc.

javadoc is built on top of the same classes as javac, and uses some of the source compiler infrastructure to implement Javadoc features.

The typical way to use javadoc is to run it against a whole package, rather than just a class.

javadoc has a very large number of switches and options that can control many aspects of its behavior. Detailed discussion of all the options is outside the scope of this book.

jdeps

The jdeps tool is a static analysis tool for analyzing the dependencies of packages or classes. The tool has a number of usages, from identifying developer code that makes calls into the internal, undocumented JDK APIs (such as the sun.misc classes) to helping trace transitive dependencies.

jdeps can also be used to confirm whether a JAR file can run under a Compact Profile (see later in the chapter for more details on Compact Profiles).

Basic usage

jdeps com.me.MyClass

Description

jdeps reports dependency information for the classes it is asked to analyze. The classes can be specified as any class on the classpath, a file path, a directory, or a JAR file.

Common switches
-s, -summary

Prints dependency summary only.

-v, -verbose

Prints all class-level dependencies.

-verbose:package

Prints package-level dependencies, excluding dependencies within the same archive.

-verbose:class

Prints class-level dependencies, excluding dependencies within the same archive.

-p <pkg name>, -package <pkg name>

Finds dependencies in the specified package. You can specify this option multiple times for different packages. The -p and -e options are mutually exclusive.

-e <regex>, -regex <regex>

Finds dependencies in packages matching the specified regular expression pattern. The -p and -e options are mutually exclusive.

-include <regex>

Restricts analysis to classes matching pattern. This option filters the list of classes to be analyzed. It can be used together with -p and -e.

-jdkinternals

Finds class-level dependencies in JDK internal APIs (which may change or disappear in even minor platform releases).

-apionly

Restricts analysis to APIs—for example, dependencies from the signature of public and protected members of public classes including field type, method parameter types, returned type, and checked exception types.

-R, -recursive

Recursively traverses all dependencies.

-h, -?, -help

Prints help message for jdeps.

Notes

jdeps is a useful tool for making developers aware of their dependencies on the JRE not as a monolithic environment, but as something more modular.

jps

Basic usage

jps jps <remote URL>

Description

jps provides a list of all active JVM processes on the local machine (or a remote machine, if a suitable instance of jstatd is running on the remote side).

Common switches
-m

Output the arguments passed to the main method

-l

Output the full package name for the application’s main class (or the full path name to the application’s JAR file)

-v

Output the arguments passed to the JVM

Notes

This command is not strictly necessary, as the standard Unix ps command could suffice. However, it does not use the standard Unix mechanism for interrogating the process, so there are circumstances where a Java process stops responding (and looks dead to jps) but is still listed as alive by the operating system.

jstat

Basic usage

jstat <pid>

Description

This command displays some basic statistics about a given Java process. This is usually a local process, but can be located on a remote machine, provided the remote side is running a suitable jstatd process.

Common switches
-options

Reports a list of report types that jstat can produce

-class

Report on classloading activity to date

-compiler

JIT compilation of the process so far

-gcutil

Detailed GC report

-printcompilation

More detail on compilation

Notes

The general syntax jstat uses to identify a process (which may be remote) is:

[<protocol>://]<vmid>[@hostname][:port][/servername]

The general syntax is used to specify a remote process (which is usually connected to via JMX over RMI), but in practice, the local syntax is far more common, which simply uses the VM ID, which is the operating system process ID (PID) on mainstream platforms (Linux, Windows, Unix, macOS, etc.).

jstatd

Basic usage

jstatd <options>

Description

jstatd provides a way of making information about local JVMs available over the network. It achieves this using RMI, and can make these otherwise-local capabilities accessible to JMX clients. This requires special security settings, which differ from the JVM defaults. To start jstatd, first we need to create the following file and name it jstatd.policy:

grant codebase "file:${java.home}../lib/tools.jar {
  permission java.security.AllPermission
}

This policy file grants all security permissions to any class loaded from the JDK’s tools.jar file.

To launch jstatd with this policy, use this command line:

jstatd -J-Djava.security.policy=<path to jstat.policy>
Common switches
-p <port>

Look for an existing RMI registry on that port, and create one if not found

Notes

It is recommended that jstatd is always switched on in production environments, but not over the public internet. For most corporate and enterprise environments, this is nontrivial to achieve and will require the cooperation of Operations and Network Engineering staff. However, the benefits of having telemetry data from production JVMs, especially during outages, are difficult to overstate.

A full discussion of JMX and monitoring techniques is outside the scope of this book.

jinfo

Basic usage

jinfo <PID> jinfo <core file>

Description

This tool displays the system properties and JVM options for a running Java process (or a core file).

Common switches
-flags

Display JVM flags only

-sysprops

Display system properties only

Notes

In practice, this is very rarely used—although it can occasionally be useful as a sanity check that the expected program is actually what is executing.

jstack

Basic usage

jstack <PID>

Description

The jstack utility produces a stack trace for each Java thread in the process.

Common switches
-F

Force a thread dump

-l

Long mode (contains additional information about locks)

Notes

Producing the stack trace does not stop or terminate the Java process. The files that jstack produces can be very large, and some post-processing of the file is usually necessary.

jmap

Basic usage

jmap <process>

Description

jmap provides a view of memory allocation for a running Java process.

Common switches
-histo

Produces a histogram of the current state of allocated memory.

-histo:live

This version of the histogram only displays information for live objects.

-heap

Produces a heap dump from the running process.

Notes

The histogram forms walk the JVMs allocation list. This includes both live and dead (but not yet collected) objects. The histogram is organized by the type of objects using memory, and is ordered from greatest to least number of bytes used by a particular type. The standard form does not pause the JVM.

The live form ensures that it is accurate, by performing a full, stop-the-world (STW) garbage collection before executing. As a result, it should not be used on a production system at a time when a full GC would appreciably impact users.

For the -heap form, note that the production of a heap dump can be a time-consuming process, and is STW. Note that for many processes, the resulting file may be extremely large.

javap

Basic usage

javap <classname>

Description

javap is the Java class disassembler—effectively a tool for peeking inside class files. It can show the bytecode that Java methods have been compiled into, as well as the “constant pool” information (which contains information similar to that of the symbol table of Unix processes).

By default, javap shows signatures of public, protected, and default methods. The -p switch will also show private methods.

Common switches
-c

Decompile bytecode

-v

Verbose mode (include constant pool information)

-p

Include private methods

Notes

The javap tool will work with any class file, provided javap is from a JDK version the same as (or later than) the one that produced the file.

Note
Some Java language features may have surprising implementations in bytecode. For example, as we saw in Chapter 9, Java’s String class has effectively immutable instances and the JVM implements the string concatenation operator + in a different way in Java versions after 8 than previously. This difference is clearly visible in the disassembled bytecode shown by javap.

jaotc

Basic usage

jaotc --output libStringHash.so StringHash.class

Description

jaotc is the ahead-of-time compiler for the Java platform—a tool for compiling Java class files or modules to native code. This can be used to create shared objects that can greatly reduce process startup time and provide lower memory footprint due to reduced dynamic class operations.

Common switches
--info

Basic information about what’s being compiled

--verbose

Verbose mode (full details)

--help

Full list of options (very useful)

Notes

The jaotc tool will work with class files, JARs, or modules and can support multiple linker backends. On macOS, the shared objects produced will be Mach .dylib files rather than Linux shared objects.

Note
Some Java language features (e.g., reflection, method handles) may have some restrictions when compiling to static code.

jlink

Basic usage

jlink [options] --module-path modulepath --add-modules module

Description

jlink is the custom runtime image linker for the Java platform—a tool for linking and packaging Java classes, modules, and their dependencies into a custom runtime image. The image created by the jlink tool will comprise a linked set of modules, along with their transitive dependences.

Common switches
--add-modules module [, module1]

Adds modules to the root set of modules to be linked

--endian {little|big}

Specifies the endianness of the target architecture

--module-path path

Specify the path where the modules for linking can be found

--save-opts file

Saves the options to the linker in the specified file

--help

Print help information

@filename

Read options from filename instead of the command line

Notes

The jlink tool will work with any class file or module and linking will require the transitive dependencies of the code to be linked.

Note
Custom runtime images don’t have any support for automatic updates by default. This means that developers are responsible for rebuilding and updating their own applications in the field when necessary. Some Java language features may have some restrictions, as the runtime image may not include the full JDK; therefore reflection and other dynamic techniques may not be fully supported.

jmod

Basic usage

jmod create [options] my-new.jmod

Description

jmod prepares Java software components for use by the custom linker (jlink). The result is a .jmod file. This should be considered an intermediate file, not a primary artifact for distribution.

Basic modes
create

Create a new JMOD file

extract

Extract all files from a JMOD file (explode it)

list

List all files from a JMOD file

describe

Print details about a JMOD file

Common switches
--module-path path

Specify the module path where the core contents of the module can be found

--libs path

Specify the path where native libraries for inclusion can be found

--help

Print help information

@filename

Read options from filename instead of the command line

Notes

jmod reads and writes the JMOD format, but please note that this is different from the modular JAR format and is not intended as an immediate replacement for it.

Note
The jmod tool is only currently intended for modules that are intended to be linked into a runtime image (using the jlink tool). One other possible use case is for packaging modules that have native libraries or other configuration files that must be distributed along with the module.

Introduction to JShell

Java is traditionally understood as a language that is class-oriented and has a distinct compile-interpret-evaluate execution model. However, in this section, we will discuss a new technology that extends this programming paradigm, by providing a form of interactive/scripting capability.

With the advent of Java 9, the Java runtime and JDK bundles a new tool, JShell. This is an interactive shell for Java, similar to the REPL seen in languages like Python, Scala, or Lisp. The shell is intended for teaching and exploratory use and, due to the nature of the Java language, is not expected to be as much use to the working programmer as similar shells are in other languages.

In particular, it is not expected that Java will become a REPL-driven language. Instead, this opens up an opportunity to use JShell for a different style of programming, one that complements the traditional use case but also provides new perspectives, especially for working with a new API.

It is very easy to use JShell to explore simple language features, for instance:

  • Primitive data types

  • Simple numeric operations

  • String manipulation basics

  • Object types

  • Defining new classes

  • Creating new objects

  • Calling methods

To start up JShell, we just invoke it from the command line:

$ jshell
|  Welcome to JShell -- Version 10
|  For an introduction type: /help intro

jshell>

From here, we can enter small pieces of Java code, which are known as snippets:

jshell> 2 * 3
$1 ==> 6

jshell> var i = 2 * 3
i ==> 6

The shell is designed to be a simple working environment, and so it relaxes some of the rules that working Java programmers may expect. Some of the differences between JShell snippets and regular Java include:

  • Semicolons are optional in JShell

  • JShell supports a verbose mode

  • JShell has a wider set of default imports than a regular Java program

  • Methods can be declared at top level (outside of a class)

  • Methods can be redefined within snippets

  • A snippet may not declare a package or a module—everything is placed in an unnamed package controlled by the shell

  • Only public classes may be accessed from JShell

  • Due to package restrictions, it’s advisable to ignore access control when defining classes and working within JShell

It’s simple to create simple class hierarchies (e.g., for exploring Java’s inheritance and generics):

jshell> class Pet {}
|  created class Pet

jshell> class Cat extends Pet {}
|  created class Cat

jshell> var c = new Cat()
c ==> Cat@2ac273d3

Tab completion within the shell is also possible, such as for autocompletion of possible methods:

jshell> c.<TAB TAB>
equals(       getClass()    hashCode()    notify()      notifyAll()
toString()    wait(

We can also create top-level methods, such as:

jshell> int div(int x, int y) {
   ...> return x / y;
   ...> }
|  created method div(int,int)

Simple exception backtraces are also supported:

jshell> div(3,0)
|  Exception java.lang.ArithmeticException: / by zero
|        at div (#2:2)
|        at (#3:1)

We can access classes from the JDK:

jshell> var ls = List.of("Alpha", "Beta", "Gamma", "Delta", "Epsilon")
ls ==> [Alpha, Beta, Gamma, Delta, Epsilon]

jshell> ls.get(3)
$11 ==> "Delta"

jshell> ls.forEach(s -> System.out.println(s.charAt(1)))
l
e
a
e
p

Or explicitly import classes if necessary:

jshell> import java.time.LocalDateTime

jshell> var now = LocalDateTime.now()
now ==> 2018-10-02T14:48:28.139422

jshell> now.plusWeeks(3)
$9 ==> 2018-10-23T14:48:28.139422

The environment also allows JShell commands, which start with a /. It is useful to be aware of some of the most common basic commands:

  • /help intro is the introductory help text

  • /help is a more comprehensive entry point into the help system

  • /vars shows which variables are in scope

  • /list will show the shell history

  • /save outputs accepted snippet source to a file

  • /open reads a saved file and brings it into the environment

For example, the imports available within JShell include a lot more than just java.lang. The whole list is loaded by JShell during startup, and can be seen as the special imports visible through the /list -all command:

jshell> /list -all

  s1 : import java.io.*;
  s2 : import java.math.*;
  s3 : import java.net.*;
  s4 : import java.nio.file.*;
  s5 : import java.util.*;
  s6 : import java.util.concurrent.*;
  s7 : import java.util.function.*;
  s8 : import java.util.prefs.*;
  s9 : import java.util.regex.*;
 s10 : import java.util.stream.*;

The JShell environment is tab-completed, which greatly adds to the usability of the tool. The verbose mode is particularly useful when you are getting to know JShell—it can be activated by passing the -v switch at startup as well as via a shell command.

Summary

Java has changed a huge amount over the last 15+ years, and yet the platform and community remain vibrant. To have achieved this, while retaining a recognizable language and platform, is no small accomplishment.

Ultimately, the continued existence and viability of Java depends upon the individual developer. On that basis, the future looks bright, and we look forward to the next wave, Java’s 25th birthday, and beyond.