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.
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.
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
javac some/package/MyClass.java
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.
-classpathSupplies classes we need for compilation.
-d some/dirTells javac where to output class files.
@project.listLoad options and source files from the file project.list.
-helpHelp on options.
-XHelp 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.
-XlintEnable detail about warnings.
-XstdoutRedirect output of compilation run to a file.
-gAdd debug information to class files.
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
java some.package.MyClass java -jar my-packaged.jar
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:
publicstaticvoidmain(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.
-cp <classpath>Define the classpath to read from.
-X, -?, -helpProvide 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.
-jarRun an executable JAR (see the entry for jar).
-Xbootclasspath(/a or /p)Run with an alternative system classpath (very rarely used).
-client, -serverSelect a HotSpot JIT compiler (see “Notes” for this entry).
-Xint, -Xcomp, -XmixedControl 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.
-verboseGenerate additional output, sometimes useful for debugging.
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
jar cvf my.jar someDir/
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.
c: Create a new archive
u: Update archive
i: Index an archive
t: List an archive
x: Extract an archive
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
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
javadoc some.package
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.
-cp <classpath>Define the classpath to use
-D <directory>Tell javadoc where to output the generated docs
-quietSuppress output except for errors and warnings
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).
jdeps com.me.MyClass
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.
-s, -summaryPrints dependency summary only.
-v, -verbosePrints all class-level dependencies.
-verbose:packagePrints package-level dependencies, excluding dependencies within the same archive.
-verbose:classPrints 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.
-jdkinternalsFinds class-level dependencies in JDK internal APIs (which may change or disappear in even minor platform releases).
-apionlyRestricts 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, -recursiveRecursively traverses all dependencies.
-h, -?, -helpPrints help message for jdeps.
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
jps jps <remote URL>
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).
-mOutput the arguments passed to the main method
-lOutput the full package name for the application’s main class (or the full path name to the application’s JAR file)
-vOutput the arguments passed to the JVM
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
jstat <pid>
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.
-optionsReports a list of report types that jstat can produce
-classReport on classloading activity to date
-compilerJIT compilation of the process so far
-gcutilDetailed GC report
-printcompilationMore detail on compilation
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
jstatd <options>
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:
grantcodebase"file:${java.home}../lib/tools.jar{permissionjava.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=<pathtojstat.policy>
-p <port>Look for an existing RMI registry on that port, and create one if not found
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
jinfo <PID> jinfo <core file>
This tool displays the system properties and JVM options for a running Java process (or a core file).
-flagsDisplay JVM flags only
-syspropsDisplay system properties only
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
jstack <PID>
The jstack utility produces a stack trace for each Java thread in the process.
-FForce a thread dump
-lLong mode (contains additional information about locks)
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
jmap <process>
jmap provides a view of memory allocation for a running Java process.
Produces a histogram of the current state of allocated memory.
This version of the histogram only displays information for live objects.
Produces a heap dump from the running process.
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
javap <classname>
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.
-cDecompile bytecode
-vVerbose mode (include constant pool information)
-pInclude private methods
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.
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
jaotc --output libStringHash.so StringHash.class
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.
--infoBasic information about what’s being compiled
--verboseVerbose mode (full details)
--helpFull list of options (very useful)
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.
jlink
jlink [options] --module-path modulepath --add-modules module
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.
--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 pathSpecify the path where the modules for linking can be found
--save-opts fileSaves the options to the linker in the specified file
--helpPrint help information
@filenameRead options from filename instead of the command line
The jlink tool will work with any class file or module and linking will require the transitive dependencies of the code to be linked.
jmod
jmod create [options] my-new.jmod
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.
createCreate a new JMOD file
extractExtract all files from a JMOD file (explode it)
listList all files from a JMOD file
describePrint details about a JMOD file
--module-path pathSpecify the module path where the core contents of the module can be found
--libs pathSpecify the path where native libraries for inclusion can be found
--helpPrint help information
@filenameRead options from filename instead of the command line
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.
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.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|WelcometoJShell--Version10|Foranintroductiontype:/helpintrojshell>
From here, we can enter small pieces of Java code, which are known as snippets:
jshell>2*3$1==>6jshell>vari=2*3i==>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>classPet{}|createdclassPetjshell>classCatextendsPet{}|createdclassCatjshell>varc=newCat()c==>Cat@2ac273d3
Tab completion within the shell is also possible, such as for autocompletion of possible methods:
jshell>c.<TABTAB>equals(getClass()hashCode()notify()notifyAll()toString()wait(
We can also create top-level methods, such as:
jshell>intdiv(intx,inty){...>returnx/y;...>}|createdmethoddiv(int,int)
Simple exception backtraces are also supported:
jshell>div(3,0)|Exceptionjava.lang.ArithmeticException:/byzero|atdiv(#2:2)|at(#3:1)
We can access classes from the JDK:
jshell>varls=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)))leaep
Or explicitly import classes if necessary:
jshell>importjava.time.LocalDateTimejshell>varnow=LocalDateTime.now()now==>2018-10-02T14:48:28.139422jshell>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-alls1:importjava.io.*;s2:importjava.math.*;s3:importjava.net.*;s4:importjava.nio.file.*;s5:importjava.util.*;s6:importjava.util.concurrent.*;s7:importjava.util.function.*;s8:importjava.util.prefs.*;s9:importjava.util.regex.*;s10:importjava.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.
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.