How to run jcmd without the JDK?

你离开我真会死。 提交于 2021-02-10 14:55:22

问题


I'm trying to figure out how to drop jcmd.exe on a windows server installed at a client site so that we can troubleshoot heap and thread issues. Don't really want to install the full JDK since it complicates the environment.

jcmd.exe definitely wants some components from the JDK to run, but I'm unable to determine which ones. If I can get it down to a small set that we unzip into a folder, use it to capture data, and then destroy, that would be perfect.

Anybody know what JDK components jcmd needs to run?


回答1:


A quick examination of jcmd.exe reveals:

    ntdll.dll => /c/WINDOWS/SYSTEM32/ntdll.dll (0x7fff01820000)
    KERNEL32.DLL => /c/WINDOWS/system32/KERNEL32.DLL (0x7ffeff180000)
    KERNELBASE.dll => /c/WINDOWS/system32/KERNELBASE.dll (0x7ffefe810000)
    SYSFER.DLL => /c/WINDOWS/System32/SYSFER.DLL (0x54f10000)
    jli.dll => /c/apps/jdk1.8.0_121/bin/jli.dll (0x51ec0000)
    MSVCR100.dll => /c/apps/jdk1.8.0_121/bin/MSVCR100.dll (0x51c40000)
    ADVAPI32.dll => /c/WINDOWS/system32/ADVAPI32.dll (0x7ffefeeb0000)
    msvcrt.dll => /c/WINDOWS/system32/msvcrt.dll (0x7fff01720000)
    sechost.dll => /c/WINDOWS/system32/sechost.dll (0x7ffeff0c0000)
    RPCRT4.dll => /c/WINDOWS/system32/RPCRT4.dll (0x7ffefec20000)
    USER32.dll => /c/WINDOWS/system32/USER32.dll (0x7ffefef60000)
    COMCTL32.dll => /c/WINDOWS/WinSxS/amd64_microsoft.windows.common-controls_6595b64144ccf1df_6.0.10586.672_none_a2d6b3cea53ff843/COMCTL32.dll (0x7ffef8460000)
    GDI32.dll => /c/WINDOWS/system32/GDI32.dll (0x7ffeff230000)
    combase.dll => /c/WINDOWS/system32/combase.dll (0x7ffeff3c0000)
    bcryptPrimitives.dll => /c/WINDOWS/system32/bcryptPrimitives.dll (0x7ffefe540000)

It therefore appears that msvcr100.dll and jli.dll would be the only requisite components from the JDK. A super quick test seems to indicate that these three files will suffice, but I will admit my test situation may not have been perfect.

EDIT: Upon further examination, here is the minimum configuration I found that works. It may be possible to modify the directory structure somewhat and set things such as CLASSPATH, JAVA_HOME, and PATH. I did not explore all of the permutations.

.:
bin/  COPYRIGHT*  jre/  lib/  LICENSE*

./bin:
jcmd.exe*  jli.dll*  msvcr100.dll*

./jre:
bin/  COPYRIGHT*  lib/  LICENSE*

./jre/bin:
attach.dll*  java.dll*  jli.dll*  net.dll*  nio.dll*  server/  unpack.dll*  
verify.dll*  zip.dll*

./jre/bin/server:
classes.jsa*  jvm.dll*  Xusage.txt*

./jre/lib:
amd64/  rt.jar*

./jre/lib/amd64:
jvm.cfg*

./lib:
jvm.lib*  tools.jar*

I also left the COPYRIGHT and LICENSE files as I felt they were important.

Test:

bin\jcmd.exe 16696 Thread.print
16696:
2017-04-27 18:01:49
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.121-b13 mixed mode):

"Worker-32" #84 prio=5 os_prio=0 tid=0x000000001bcaf800 nid=0x416c in 
Object.wait() [0x00000000335ef000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    at org.eclipse.core.internal.jobs.WorkerPool.sleep(WorkerPool.java:188)
    - locked <0x00000000c21120a8> (a org.eclipse.core.internal.jobs.WorkerPool)
    at org.eclipse.core.internal.jobs.WorkerPool.startJob(WorkerPool.java:220)
    at org.eclipse.core.internal.jobs.Worker.run(Worker.java:52)

"Worker-31" #83 prio=5 os_prio=0 tid=0x000000001bcb7800 nid=0x315c in 
Object.wait() [0x00000000312ef000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    at org.eclipse.core.internal.jobs.WorkerPool.sleep(WorkerPool.java:188)
    - locked <0x00000000c21120a8> (a org.eclipse.core.internal.jobs.WorkerPool)
    at org.eclipse.core.internal.jobs.WorkerPool.startJob(WorkerPool.java:220)
    at org.eclipse.core.internal.jobs.Worker.run(Worker.java:52)

<snip>...


F:\tmp\t1>bin\jcmd 16696 VM.flags
16696:
-XX:CICompilerCount=4 -XX:InitialHeapSize=268435456 
-XX:MaxHeapSize=1073741824 -XX:MaxNewSize=357564416 
-XX:MinHeapDeltaBytes=524288 -XX:NewSize=89128960 -XX:OldSize=179306496 
-XX:+UseCompressedClassPointers -XX:+UseCompressedOops 
-XX:+UseFastUnorderedTimeStamps -XX:-UseLargePagesIndividualAllocation 
-XX:+UseParallelGC



回答2:


The jcmd tool is written in Java, so a JDK is needed.

https://github.com/openjdk/jdk/blob/master/src/jdk.jcmd/share/classes/sun/tools/jcmd/JCmd.java

From JDK 9 or later, you can use jlink to create a custom JDK image that only contains modules needed for the jcmd tool.

$ cd %JAVA_HOME%
$ bin\jlink --module-path jmods --add-modules jdk.jcmd --output c:\custom-jdk
$ c:\custom-jdk\bin
$ jcmd 

It's about 40 MB on my machine and it can operate against earlier releases. Not sure how much it helps, but it is the clean way to do it.




回答3:


I was also looking for a way to run jcmd with JRE (on production). I could not use the other existing answer from @KevinO because I'm running on K8S and my docker image is based on Alpine Linux.

Eventually, I ended up with copying the minimal artifacts from JDK into the docker image and I was able to run jcmd and start Flight Recorder. I'm using Azul JRE zulu11.41.24-sa-jre11.0.8-linux_musl_x64.

Here is what worked for me (snippet from my dockerfile):

RUN  tar zxf /tmp/${JDK}.tar.gz -C /tmp && \
        mkdir -p /jcmd && \
        mkdir -p /jcmd/bin && \
        mkdir -p /jcmd/lib && \
        cp /tmp/${JDK}/bin/jcmd /jcmd/bin/jcmd && \
        cp -r /tmp/${JDK}/lib/libattach.so /tmp/${JDK}/lib/jvm.cfg /tmp/${JDK}/lib/libjava.so /tmp/${JDK}/lib/libjimage.so /tmp/${JDK}/lib/libnet.so /tmp/${JDK}/lib/libnio.so /tmp/${JDK}/lib/libverify.so /tmp/${JDK}/lib/libzip.so /tmp/${JDK}/lib/modules /tmp/${JDK}/lib/server /tmp/${JDK}/lib/jli /jcmd/lib && \
        rm -rf /tmp/${JDK} 


来源:https://stackoverflow.com/questions/43644361/how-to-run-jcmd-without-the-jdk

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!