javajava-11java-platform-module-systemjlinkjdeps

How to get Java class' real module dependency?


My JDK version is OpenJDK 11. My class File is jmx.Main.class

Here are my code.

package jmx;
import java.lang.management.ManagementFactory;

import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.ReflectionException;

public class Main {
    public static void main(String[] args) throws InstanceNotFoundException, AttributeNotFoundException, MalformedObjectNameException, ReflectionException, MBeanException  {
        /* Total number of processors or cores available to the JVM */
        MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
        Object attribute = mBeanServer.getAttribute(new ObjectName("java.lang","type","OperatingSystem"), "TotalPhysicalMemorySize");
        Object attribute2 = mBeanServer.getAttribute(new ObjectName("java.lang","type","OperatingSystem"), "FreePhysicalMemorySize");
        System.out.println("Total memory: "+ Long.parseLong(attribute.toString()) / 1024  +"MB");
        System.out.println("Free  memory: "+ Long.parseLong(attribute2.toString()) / 1024  +"MB");
    }
}

It works fine when it runs in IDE. I want to use a custom jre. Then I use jdeps to analyze dependencies. The result is:

Main.class -> java.base
Main.class -> java.management
   jmx                                        -> java.io                                            java.base
   jmx                                        -> java.lang                                          java.base
   jmx                                        -> java.lang.invoke                                   java.base
   jmx                                        -> java.lang.management                               java.management
   jmx                                        -> javax.management                                   java.management

So I think java.base and java.management is the dependency modules.

Then I use jlink to generate my custom jre.

jlink --module-path "C:\Program Files\Java\jdk-11.0.6\jmods" --add-modules java.base,java.management --output jre11

Before using my custom jre, I had run my code in cmd windows. It works fine.

Then I run the code in my jre. The code can't run and I got an error:

javax.management.AttributeNotFoundException: No such attribute: TotalPhysicalMemorySize

So I think the reason is that some dependency modules is missing. I run jlink to generate a whole module jre. When I use the whole module jre, the code runs correctly again.

How can I get the real dependency modules? or It's a JDK's bug?


Solution

  • Before JDK 9, the type com.sun.management.OperatingSystemMXBean was an undocumented extension of java.lang.management.OperatingSystemMXBean. So it was reasonable to use a reflective attribute query like getAttribute(new ObjectName("java.lang","type","OperatingSystem"), "TotalPhysicalMemorySize") which does not create a dependency to nonstandard APIs.

    The advantage is also the disadvantage. When there is no dependency, a tool to analyse dependencies can’t detect a dependency.

    When you add the module jdk.management, the extension will be available. Being part of a documented module also implies that when you are willing to accept a permanent dependency to the module, you can use the extended OperatingSystemMXBean directly.

    import com.sun.management.OperatingSystemMXBean;
    import java.lang.management.ManagementFactory;
    
    public class OSMX {
        public static void main(String[] args) {
            OperatingSystemMXBean osBean
                = ManagementFactory.getPlatformMXBean(OperatingSystemMXBean.class);
            System.out.println("Total memory: "
                + osBean.getTotalPhysicalMemorySize() / (1024*1024)  +"MB");
            System.out.println("Free  memory: "
                + osBean.getFreePhysicalMemorySize() / (1024*1024)  +"MB");
        }
    }
    

    Then, jdeps will report the dependency to jdk.management correctly.