I Used Five Command Line Java Tools This Week and I Feel Good
The tools: jps, jmap, jshell, jdeps, jdeprscan
I Used Five Command Line Java Tools This Week and I Feel Good
The tools: [jps](https://docs.oracle.com/en/java/javase/25/docs/specs/man/jps.html), [jmap](https://docs.oracle.com/en/java/javase/25/docs/specs/man/jmap.html), [jshell](https://docs.oracle.com/en/java/javase/25/docs/specs/man/jshell.html), [jdeps](https://docs.oracle.com/en/java/javase/25/docs/specs/man/jdeps.html), [jdeprscan](https://docs.oracle.com/en/java/javase/25/docs/specs/man/jdeprscan.html)
Photo by Fotis Nakos on Unsplash
I spend most of my time working in the IntelliJ IDEA IDE. This week I took a break from my IDE and spent some time working with [jps](https://docs.oracle.com/en/java/javase/25/docs/specs/man/jps.html), [jmap](https://docs.oracle.com/en/java/javase/25/docs/specs/man/jmap.html), [jshell](https://docs.oracle.com/en/java/javase/25/docs/specs/man/jshell.html), [jdeps](https://docs.oracle.com/en/java/javase/25/docs/specs/man/jdeps.html), and [jdeprscan](https://docs.oracle.com/en/java/javase/25/docs/specs/man/jdeprscan.html) on the command line.
If you don’t know what they are useful for, then click the links I’ve provided to each tool. I will be continuing to update this blog with examples for each command, but wanted to get you started with some useful info. Stay tuned!
See examples for each of the commands in the following sections.
1. jps
Shows you all the Java processes running with their process ids. I was running IntelliJ IDEA (1642 Main) and JShell (1821 and 1822) when I executed the command.
$ jps
1825 Jps
1642 Main
1821 jdk.internal.jshell.tool.JShellToolProvider
1822 RemoteExecutionControl
2. jmap
You can use this to get a Java heap dump or a histogram of a Java heap with classes, memory in bytes, and number of instances for each type. I use this tool as an x-ray for Java heaps that have memory challenges. I have been using jmap for 22 years. It’s a simple tool, but very effective. Also, because it is command line, you can use it easily with GenAI tools.
$ jmap -histo 1642 | head -n 12
num #instances #bytes class name (module)
-------------------------------------------------------
1: 2694831 200036600 [B (java.base@21.0.8)
2: 9119099 145905584 java.lang.Integer (java.base@21.0.8)
3: 3770944 120845536 [Ljava.lang.Object; (java.base@21.0.8)
4: 4536329 108871896 kotlin.Pair
5: 2234397 53625528 java.lang.String (java.base@21.0.8)
6: 343200 44834360 [I (java.base@21.0.8)
7: 11400 40318360 [Ljdk.internal.vm.FillerElement; (java.base@21.0.8)
8: 65054 26567248 [J (java.base@21.0.8)
9: 1220235 19523760 com.intellij.platform.workspace.jps.entities.SdkDependency
10: 739476 17747424 com.intellij.core.rwmutex.ReadPermitImpl
One thing that jumps out at me here is the number on Integer wrapper instances and kotlin.Pair instances. Looking at the instance counts, I would guess that these are Pair<Integer, Integer> because there are 2x more Integer instances than Pair. If this is the case, I would use an IntIntPair object from a library like Eclipse Collections.
3. jshell
You can use jshell as a REPL to execute arbitrary Java code. You can add third-party libraries to the classpath. Use the --class-path parameter to add them. On Mac/Linux, separate jar files with a “:” (colon). On Windows, separate them with a “;” (semi-colon).
The following will run jshell with the complete Eclipse Collections library (api and impl) to the class-path.
$ jshell --class-path
~/.m2/repository/org/eclipse/collections/eclipse-collections/13.0.0/eclipse-collections-13.0.0.jar
:~/.m2/repository/org/eclipse/collections/eclipse-collections-api/13.0.0/eclipse-collections-api-13.0.0.jar
| Welcome to JShell -- Version 25.0.1
| For an introduction type: /help intro
jshell> import org.eclipse.collections.impl.factory.*
jshell> import org.eclipse.collections.api.list.*
jshell> List<Integer> list = Lists.mutable.with(1, 2, 3, 4, 5)
list ==> [1, 2, 3, 4, 5]
jshell> List<Integer> evensStream = list.stream().filter(each -> each % 2 == 0).toList()
evensStream ==> [2, 4]
jshell> MutableList<Integer> mutableList = Lists.mutable.with(1, 2, 3, 4, 5)
mutableList ==> [1, 2, 3, 4, 5]
jshell> MutableList<Integer> evens = mutableList.select(each -> each % 2 ==0)
evens ==> [2, 4]
jshell> ImmutableList<Integer> immutableList = Lists.immutable.with(1, 2, 3, 4, 5)
immutableList ==> [1, 2, 3, 4, 5]
jshell> ImmutableList<Integer> odds = immutableList.reject(each -> each % 2 == 0)
odds ==> [1, 3, 5]
jshell> List<Integer> oddsStream = list.stream().filter(each -> each % 2 != 0).toList()
oddsStream ==> [1, 3, 5]
jshell>
If you want to use jshell with Eclipse Collections, you might find this blog helpful, which has the entire package hierarchy for Eclipse Collections in mind map visualizations. This was very helpful for me this week, as there is a bug in jshell that has been apparently fixed in Java 26, for tab completion of imports in third-party jars.
Tab Completion for Third Party Jar imports
I discovered that tab completion doesn’t work in jshell using Java 25 for importing classes using tab completion, and that’s why the mind map visualizations for Eclipse Collections came in handy. It looks like this bug which was reported in Java 11 has finally been fixed in Java 26. Please comment on this blog if you can confirm.
[embed]Loading... In 9b136, it works well like: $ ~/bin/jdk9b136/bin/jshell --class-path…bugs.openjdk.org
Passing JVM Runtime Parameters to JShell
I learned this week there are two processes started for JShell, and there are two flags you can use to pass JVM runtime parameters to each of them separately. There is a -Jflag and -Rflag. The -R one is useful if you want to the see the effect in the code you write. It took me a while to figure this out. Look at the jshell command options here.
The option I wanted to try was setting Compact Object Headers on since it is still an optional JVM parameter. I will show you how to run JShell with this option on, and also how to run it with Java Object Layout (JOL) on your classpath so you can write code to see the impact.
a. Using JShell w/ JOL on classpath w/ no settings
$ jshell
--class-path ~/.m2/repository/org/openjdk/jol/jol-core/0.17/jol-core-0.17.jar
| Welcome to JShell -- Version 25.0.1
| For an introduction type: /help intro
jshell> import org.openjdk.jol.info.*
jshell> System.out.println(
GraphLayout.parseInstance(List.of().stream().filter(each -> true))
.toFootprint())
# WARNING: Unable to get Instrumentation. Dynamic Attach failed. You may add this JAR as -javaagent manually, or supply -Djdk.attach.allowAttachSelf
# WARNING: Unable to attach Serviceability Agent. You can try again with escalated privileges. Two options: a) use -Djol.tryWithSudo=true to try with sudo; b) echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
java.util.stream.ReferencePipeline$2@41cf53f9d footprint:
COUNT AVG SUM DESCRIPTION
1 16 16 REPL.$JShell$3$$Lambda/0x00000ffc01048000
1 16 16 [Ljava.lang.Object;
1 32 32 java.util.AbstractList$RandomAccessSpliterator
1 24 24 java.util.ImmutableCollections$ListN
1 56 56 java.util.stream.ReferencePipeline$2
1 56 56 java.util.stream.ReferencePipeline$Head
6 200 (total)
b. Using JShell w/ JOL on classpath w/ Compact Object Headers enabled
$ jshell
--class-path ~/.m2/repository/org/openjdk/jol/jol-core/0.17/jol-core-0.17.jar
-R-XX:+UseCompactObjectHeaders
| Welcome to JShell -- Version 25.0.1
| For an introduction type: /help intro
jshell> import org.openjdk.jol.info.*
jshell> System.out.println(
GraphLayout.parseInstance(List.of().stream().filter(each -> true))
.toFootprint())
# WARNING: Unable to get Instrumentation. Dynamic Attach failed. You may add this JAR as -javaagent manually, or supply -Djdk.attach.allowAttachSelf
# WARNING: Unable to attach Serviceability Agent. You can try again with escalated privileges. Two options: a) use -Djol.tryWithSudo=true to try with sudo; b) echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
java.util.stream.ReferencePipeline$2@5a10411d footprint:
COUNT AVG SUM DESCRIPTION
1 8 8 REPL.$JShell$3$$Lambda/0x00000fc001049400
1 16 16 [Ljava.lang.Object;
1 32 32 java.util.AbstractList$RandomAccessSpliterator
1 16 16 java.util.ImmutableCollections$ListN
1 56 56 java.util.stream.ReferencePipeline$2
1 48 48 java.util.stream.ReferencePipeline$Head
6 176 (total)
4. jdeps
Analyzes and reports on dependencies on classes. Using the -s parameter gives a high-level summary as seen below
$ jdeps -s ~/.m2/repository/org/openjdk/jol/jol-core/0.17/jol-core-0.17.jar
# I abbreviated JDK info and replaced with <JDK Version>
jol-core-0.17.jar -> ~<JDK Version>/Contents/Home/jre/lib/rt.jar
Using jdeps with no parameters and just a target .jar file will show package dependencies.
$ jdeps ~/.m2/repository/org/openjdk/jol/jol-core/0.17/jol-core-0.17.jar
# I abbreviated JDK info and replaced with <JDK Version>
jol-core-0.17.jar -> ~<JDK Version>/Contents/Home/jre/lib/rt.jar
org.openjdk.jol.datamodel (jol-core-0.17.jar)
-> java.lang
-> java.util
-> org.openjdk.jol.vm jol-core-0.17.jar
org.openjdk.jol.heap (jol-core-0.17.jar)
-> java.io
-> java.lang
-> java.nio
-> java.util
-> java.util.zip
-> org.openjdk.jol.info jol-core-0.17.jar
-> org.openjdk.jol.util jol-core-0.17.jar
org.openjdk.jol.info (jol-core-0.17.jar)
-> java.awt
-> java.awt.geom
-> java.awt.image
-> java.io
-> java.lang
-> java.lang.invoke
-> java.lang.ref
-> java.lang.reflect
-> java.util
-> java.util.function
-> javax.imageio
-> org.openjdk.jol.datamodel jol-core-0.17.jar
-> org.openjdk.jol.layouters jol-core-0.17.jar
-> org.openjdk.jol.util jol-core-0.17.jar
-> org.openjdk.jol.vm jol-core-0.17.jar
org.openjdk.jol.layouters (jol-core-0.17.jar)
-> java.lang
-> java.lang.reflect
-> java.util
-> org.openjdk.jol.datamodel jol-core-0.17.jar
-> org.openjdk.jol.info jol-core-0.17.jar
-> org.openjdk.jol.util jol-core-0.17.jar
-> org.openjdk.jol.vm jol-core-0.17.jar
org.openjdk.jol.util (jol-core-0.17.jar)
-> java.io
-> java.lang
-> java.lang.reflect
-> java.net
-> java.util
-> org.openjdk.jol.vm jol-core-0.17.jar
org.openjdk.jol.vm (jol-core-0.17.jar)
-> java.io
-> java.lang
-> java.lang.annotation
-> java.lang.instrument
-> java.lang.invoke
-> java.lang.management
-> java.lang.reflect
-> java.net
-> java.security
-> java.util
-> java.util.function
-> java.util.jar
-> java.util.zip
-> javax.management
-> javax.management.openmbean
-> org.openjdk.jol.info jol-core-0.17.jar
-> org.openjdk.jol.layouters jol-core-0.17.jar
-> org.openjdk.jol.util jol-core-0.17.jar
-> org.openjdk.jol.vm.sa jol-core-0.17.jar
-> sun.misc JDK internal API (rt.jar)
org.openjdk.jol.vm.sa (jol-core-0.17.jar)
-> java.io
-> java.lang
-> java.lang.invoke
-> java.lang.management
-> java.lang.reflect
-> java.util
-> java.util.concurrent
-> org.openjdk.jol.util jol-core-0.17.jar
-> sun.management JDK internal API (rt.jar)
Using the -v option gives a verbose class level dependency report which may be voluminous.
$ jdeps -v ~/.m2/repository/org/openjdk/jol/jol-core/0.17/jol-core-0.17.jar
# I abbreviated JDK info and replaced with <JDK Version>
jol-core-0.17.jar -> ~<JDK Version>/Contents/Home/jre/lib/rt.jar
org.openjdk.jol.datamodel.DataModel -> java.lang.Object
org.openjdk.jol.datamodel.DataModel -> java.lang.String
org.openjdk.jol.datamodel.Model32 -> java.lang.Class
org.openjdk.jol.datamodel.Model32 -> java.lang.Integer
org.openjdk.jol.datamodel.Model32 -> java.lang.Object
org.openjdk.jol.datamodel.Model32 -> java.lang.String
org.openjdk.jol.datamodel.Model32 -> java.lang.StringBuilder
org.openjdk.jol.datamodel.Model32 -> java.util.Objects
org.openjdk.jol.datamodel.Model32 -> org.openjdk.jol.datamodel.DataModel jol-core-0.17.jar
org.openjdk.jol.datamodel.Model64 -> java.lang.Class
org.openjdk.jol.datamodel.Model64 -> java.lang.Integer
org.openjdk.jol.datamodel.Model64 -> java.lang.Object
org.openjdk.jol.datamodel.Model64 -> java.lang.String
org.openjdk.jol.datamodel.Model64 -> java.lang.StringBuilder
org.openjdk.jol.datamodel.Model64 -> java.util.Objects
org.openjdk.jol.datamodel.Model64 -> org.openjdk.jol.datamodel.DataModel jol-core-0.17.jar
org.openjdk.jol.datamodel.Model64_Lilliput -> java.lang.Class
org.openjdk.jol.datamodel.Model64_Lilliput -> java.lang.Object
org.openjdk.jol.datamodel.Model64_Lilliput -> java.lang.String
org.openjdk.jol.datamodel.Model64_Lilliput -> java.lang.StringBuilder
org.openjdk.jol.datamodel.Model64_Lilliput -> org.openjdk.jol.datamodel.DataModel jol-core-0.17.jar
org.openjdk.jol.datamodel.ModelVM -> java.lang.Class
org.openjdk.jol.datamodel.ModelVM -> java.lang.Object
org.openjdk.jol.datamodel.ModelVM -> java.lang.String
org.openjdk.jol.datamodel.ModelVM -> org.openjdk.jol.datamodel.DataModel jol-core-0.17.jar
org.openjdk.jol.datamodel.ModelVM -> org.openjdk.jol.vm.VM jol-core-0.17.jar
org.openjdk.jol.datamodel.ModelVM -> org.openjdk.jol.vm.VirtualMachine jol-core-0.17.jar
org.openjdk.jol.heap.HeapDumpException -> java.lang.Exception
org.openjdk.jol.heap.HeapDumpException -> java.lang.String
org.openjdk.jol.heap.HeapDumpReader -> java.io.BufferedInputStream
org.openjdk.jol.heap.HeapDumpReader -> java.io.ByteArrayOutputStream
org.openjdk.jol.heap.HeapDumpReader -> java.io.File
org.openjdk.jol.heap.HeapDumpReader -> java.io.FileInputStream
org.openjdk.jol.heap.HeapDumpReader -> java.io.IOException
org.openjdk.jol.heap.HeapDumpReader -> java.io.InputStream
org.openjdk.jol.heap.HeapDumpReader -> java.lang.Integer
org.openjdk.jol.heap.HeapDumpReader -> java.lang.Long
org.openjdk.jol.heap.HeapDumpReader -> java.lang.Math
org.openjdk.jol.heap.HeapDumpReader -> java.lang.Object
org.openjdk.jol.heap.HeapDumpReader -> java.lang.String
.... abbreviated for readability.
5. jdeprscan
Scans a directory, jar file, or class for usage of deprecated APIs.
$ jdeprscan ~/.m2/repository/org/openjdk/jol/jol-core/0.17/jol-core-0.17.jar
Jar file /Users/donaldraab/.m2/repository/org/openjdk/jol/jol-core/0.17/jol-core-0.17.jar:
class org/openjdk/jol/vm/VM uses deprecated class java/security/AccessController (forRemoval=true)
Thanks for reading!
I am the creator of and committer for the Eclipse Collections OSS project, which is managed at the Eclipse Foundation. Eclipse Collections is open for contributions. I am the author of the book, Eclipse Collections Categorically: Level up your programming game.
메타데이터
- post_id
- a07531fefb02
- slug
- i-used-five-command-line-java-tools-this-week-and-i-feel-good-a07531fefb02
- url
- https://medium.com/@donraab/i-used-five-command-line-java-tools-this-week-and-i-feel-good-a07531fefb02
- canonical_url
- https://medium.com/@donraab/i-used-five-command-line-java-tools-this-week-and-i-feel-good-a07531fefb02
- author_url
- https://medium.com/@donraab
- status
- ok
- fetched_at
- 2026-07-11 01:01:15