Skip to main content

JVM源码解读-Runtime类

moremind...About 10 minJava-SourceJava-Source

1.类图

2.核心方法

2.1 exit方法

2.1.1 源码

public void exit(int status) {
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkExit(status);
    }
    Shutdown.exit(status);
}

2.1.2 说明

该方法作用是终止当前正在运行的Java虚拟机,这个status表示退出的状态码,非零表示异常终止。JVM的关闭序列首先调用所有已注册的关闭挂钩,并等待它们完成。然后,如果启用了退出完成,它将运行所有未调用的终结器。最后,它停止了JVM。

  • exit(0):正常退出,程序正常执行结束退出,Java GC进行垃圾回收,直接退出。
  • exit(1):是非正常退出,就是说无论程序正在执行与否,都退出。1.如果为非0的话,如果这个方法被调用后,虚拟机已开始关闭序列如果关闭钩子正在运行,此方法将无限期阻塞。
    2.如果钩子运行完成,并且未调用的finalizers,在finalization-on-exit允许的情况下启动回收完成,虚拟机停止。

2.2 addShutdownHook方法

2.2.1 源码

public void addShutdownHook(Thread hook) {
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        sm.checkPermission(new RuntimePermission("shutdownHooks"));
    }
    ApplicationShutdownHooks.add(hook);
}

2.2.2 说明

jvm中增加一个关闭的钩子,当jvm关闭的时候,会执行系统中已经设置的所有通过方法addShutdownHook添加的钩子,当系统执行完这些钩子后,jvm才会关闭。所以这些钩子可以在jvm关闭的时候进行内存清理、对象销毁等操作。

2.2.3 示例

public class RuntimeDemo {
    @Test
    public void test2() {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.out.println("优雅的关闭");
            executor.shutdown();
        }));
    }
}

2.3 removeShutdownHook方法

2.3.1 源码

public boolean removeShutdownHook(Thread hook) {
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        sm.checkPermission(new RuntimePermission("shutdownHooks"));
    }
    return ApplicationShutdownHooks.remove(hook);
}

2.3.2 说明

用来关闭添加的钩子已经添加的钩子

2.3.3 示例

public class RuntimeDemo {
    @Test
    public void test3() {
        Thread thread = new Thread(() -> System.out.println("Do something1 in Shutdown Hook"), "test");
        Runtime.getRuntime().addShutdownHook(thread);
        Runtime.getRuntime().addShutdownHook(new Thread(() -> System.out.println("Do something2 in Shutdown Hook2"), "test2"));
        System.out.println("hello world");
        Runtime.getRuntime().removeShutdownHook(thread);
    }
}

2.4 halt方法

2.4.1 源码

public void halt(int status) {
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        sm.checkExit(status);
    }
    Shutdown.beforeHalt();
    Shutdown.halt(status);
}

2.4.2 说明

  • 可用于强制终止正在运行的JVM, 与exit方法不同,此方法不会触发JVM关闭序列。 因此,当我们调用halt方法时,都不会执行关闭钩子或终结器.

2.4.3 示例

public class RuntimeDemo {
    @Test
    public void test4() {
        Thread thread = new Thread(() -> System.out.println("Do something1 in Shutdown Hook"), "test");
        Runtime.getRuntime().addShutdownHook(thread);
        System.out.println("hello world");
        Runtime.getRuntime().removeShutdownHook(thread);
        Runtime.getRuntime().halt(0);
    }
}

2.5 runFinalization方法

2.5.1 源码

public void runFinalization() {
    runFinalization0();
}

private static native void runFinalization0();

2.5.2 说明

java.lang.Runtime.runFinalization() 方法运行任何等待终结的对象的终结方法。 调用此方法表明 Java 虚拟机花费精力运行已发现已丢弃但尚未运行其 finalize 方法的对象的 finalize 方法。 当控制从方法调用返回时,虚拟机已尽最大努力完成所有未完成的终结。

如果未显式调用 runFinalization 方法,虚拟机会根据需要在单独的线程中自动执行终结过程。 System.runFinalization() 方法是调用此方法的常规且方便的方法。

2.5.3 说明

public class RuntimeDemo {
    @Test
    public void test5() {
        System.out.println("Program is starting...");
        System.out.println("Running Finalization...");
        Runtime.getRuntime().runFinalization();
        System.out.println("Done.");
        System.out.println("Test.");
    }
}

2.6 exec方法

2.6.1 源码

public class Runtime {
    public Process exec(String command) throws IOException {
        return exec(command, null, null);
    }
    public Process exec(String command, String[] envp) throws IOException {
        return exec(command, envp, null);
    }
    public Process exec(String command, String[] envp, File dir)
            throws IOException {
        if (command.isEmpty())
            throw new IllegalArgumentException("Empty command");
        
        StringTokenizer st = new StringTokenizer(command);
        String[] cmdarray = new String[st.countTokens()];
        for (int i = 0; st.hasMoreTokens(); i++)
            cmdarray[i] = st.nextToken();
        return exec(cmdarray, envp, dir);
    }
    public Process exec(String cmdarray[]) throws IOException {
        return exec(cmdarray, null, null);
    }
    public Process exec(String[] cmdarray, String[] envp) throws IOException {
        return exec(cmdarray, envp, null);
    }
    public Process exec(String[] cmdarray, String[] envp, File dir)
            throws IOException {
        return new ProcessBuilder(cmdarray)
                .environment(envp)
                .directory(dir)
                .start();
    }
}

2.6.2 说明

  • java.lang.Runtime.exec(String command) 方法在单独的进程中执行指定的字符串命令。 这是一种方便的方法。 调用 exec(command) 形式的行为与调用 exec(command, null, null) 完全相同。

  • java.lang.Runtime.exec(String[] cmdarray) 方法在单独的进程中执行指定的命令和参数。 这是一种方便的方法。 exec(cmdarray) 形式的调用与调用 exec(cmdarray, null, null) 的行为方式完全相同。

  • java.lang.Runtime.exec(String[] cmdarray, String[] envp) 方法在具有指定环境的单独进程中执行指定的命令和参数。 这是一种方便的方法。 exec(cmdarray, envp) 形式的调用与调用 exec(cmdarray, envp, null) 的行为方式完全相同。

  • java.lang.Runtime.exec(String command, String[] envp) 方法在具有指定环境的单独进程中执行指定的字符串命令。 这是一种方便的方法。 exec(command, envp) 形式的调用与调用 exec(command, envp, null) 的行为方式完全相同。

  • java.lang.Runtime.exec(String command, String[] envp, File dir) 方法在具有指定环境和工作目录的单独进程中执行指定的字符串命令。 这是一种方便的方法。 exec(command, envp, dir) 形式的调用与 exec(cmdarray, envp, dir) 调用的行为方式完全相同,其中 cmdarray 是命令中所有标记的数组。
    更准确地说,命令字符串使用由调用 new StringTokenizer(command) 创建的 StringTokenizer 分解为标记,而无需进一步修改字符类别。 标记器生成的标记然后以相同的顺序放置在新的字符串数组 cmdarray 中。

2.6.3 示例

public class RuntimeDemo {
    @Test
    public void test6() throws IOException {
        // create a new array of 2 strings
        String[] cmdArray = new String[2];
        // first argument is the program we want to open
        cmdArray[0] = "notepad.exe";
        // second argument is a txt file we want to open with notepad
        cmdArray[1] = "example.txt";
        // print a message
        System.out.println("Executing notepad.exe and opening example.txt");
        // create a process and execute cmdArray
        Process process = Runtime.getRuntime().exec(cmdArray);
        File dir = new File("C:/");
        // create a process and execute notepad.exe and currect environment
        Process process2 = Runtime.getRuntime().exec("notepad.exe", null, dir);
        // print another message
        System.out.println("example.txt should now open.");
    }
}

2.7 availableProcessors方法

public native int availableProcessors();

此方法返回 Java 虚拟机可用的处理器数量。

public class RuntimeDemo {
    @Test
    public void test7() {
        System.out.println(Runtime.getRuntime().availableProcessors());
    }
}

2.8 freeMemory方法

public native long freeMemory();

java.lang.Runtime.freeMemory() 方法返回 Java 虚拟机中的可用内存量。 调用 gc 方法可能会导致 freeMemory 返回的值增加。

public class RuntimeDemo {
    @Test
    public void test8() {
        // 231296640
        System.out.println(Runtime.getRuntime().freeMemory());
    }
}

2.9 maxMemory方法

public native long maxMemory();

java.lang.Runtime.maxMemory() 方法返回 Java 虚拟机将尝试使用的最大内存量。 如果没有固有限制,则返回 Long.MAX_VALUE 值。

public class RuntimeDemo {
    @Test
    public void test9() {
        // 3782737920
        System.out.println(Runtime.getRuntime().maxMemory());
    }
}

2.10 totalMemory方法

public native long totalMemory();

java.lang.Runtime.totalMemory() 方法返回 Java 虚拟机中的内存总量。 此方法返回的值可能会随时间变化,具体取决于主机环境。 请注意,保存任何给定类型的对象所需的内存量可能取决于实现。

public class RuntimeDemo {
    @Test
    public void test10() {
        // 255328256
        System.out.println(Runtime.getRuntime().totalMemory());
    }
}

2.11 gc方法

public native void gc();
  • java.lang.Runtime.gc() 方法运行垃圾收集器。 调用此方法表明 Java 虚拟机花费精力回收未使用的对象,以使它们当前占用的内存可用于快速重用。 当控制从方法调用返回时,虚拟机已尽最大努力回收所有丢弃的对象。

  • 名称 gc 代表"垃圾收集器"。 虚拟机根据需要在单独的线程中自动执行此回收过程,即使没有显式调用 gc 方法。 System.gc() 方法是调用此方法的常规且方便的方法。

  • 其实基本没什么机会用得到这个命令, 因为这个命令只是建议JVM安排GC运行, 还有可能完全被拒绝。
    GC本身是会周期性的自动运行的,由JVM决定运行的时机,而且现在的版本有多种更智能的模式可以选择,还会根据运行的机器自动去做选择,就算真的有性能上的需求,也应该去对GC的运行机制进行微调,而不是通过使用这个命令来实现性能的优化

2.11 traceInstructions方法

2.11.1 源码

public native void traceInstructions(boolean on);

2.11.2 说明

java.lang.Runtime.traceInstructions(boolean on) 方法启用/禁用指令跟踪。 如果布尔参数为真,则此方法建议 Java 虚拟机在执行时为虚拟机中的每条指令发出调试信息。 此信息的格式以及将其发送到的文件或其他输出流取决于主机环境。 如果虚拟机不支持此功能,它可能会忽略此请求。跟踪输出的目的地取决于系统。如果布尔参数为假,此方法会导致虚拟机停止执行它正在执行的详细指令跟踪。

2.12 traceMethodCalls方法

2.12.1 源码

public native void traceMethodCalls(boolean on);

2.12.2 说明

java.lang.Runtime.traceMethodCalls(boolean on) 方法启用/禁用方法调用的跟踪。如果布尔参数为真,则此方法建议 Java 虚拟机在调用虚拟机时为虚拟机中的每个方法发出调试信息。此信息的格式以及将其发送到的文件或其他输出流取决于主机环境。如果虚拟机不支持此功能,它可能会忽略此请求。使用参数 false 调用此方法表明虚拟机停止发出每次调用的调试信息。

2.13 load方法

2.13.1 源码

public class Runtime {
    @CallerSensitive
    public void load(String filename) {
        load0(Reflection.getCallerClass(), filename);
    }
    
    synchronized void load0(Class<?> fromClass, String filename) {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkLink(filename);
        }
        if (!(new File(filename).isAbsolute())) {
            throw new UnsatisfiedLinkError(
                    "Expecting an absolute path of the library: " + filename);
        }
        ClassLoader.loadLibrary(fromClass, filename, true);
    }
}

2.13.2 说明

java.lang.Runtime.load(String filename) 方法将指定的文件名加载为动态库。 文件名参数必须是完整的路径名,(例如 Runtime.getRuntime().load()。首先,如果有安全管理器,则以文件名作为参数调用其 checkLink 方法。这可能会导致安全 异常。这类似于方法 loadLibrary(String),但它接受一个通用文件名作为参数,而不仅仅是一个库名,允许加载任何本机代码文件。方法 System.load(String) 是 调用此方法的常规和方便的方法。

2.13.3 示例

public class RuntimeDemo {
    @Test
    public void test12() {
        // print when the program starts
        System.out.println("Program starting...");
        
        // load a library that is Windows/System32 folder
        System.out.println("Loading Library...");
        Runtime.getRuntime().load("C:/Windows/System32/crypt32.dll");
        System.out.println("Library Loaded.");
    }
}

2.14 loadLibrary方法

2.14.1 源码

public class Runtime {
    @CallerSensitive
    public void loadLibrary(String libname) {
        loadLibrary0(Reflection.getCallerClass(), libname);
    }
    
    synchronized void loadLibrary0(Class<?> fromClass, String libname) {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkLink(libname);
        }
        if (libname.indexOf((int)File.separatorChar) != -1) {
            throw new UnsatisfiedLinkError(
                    "Directory separator should not appear in library name: " + libname);
        }
        ClassLoader.loadLibrary(fromClass, libname, false);
    }
}

2.14.2 说明

java.lang.Runtime.loadLibrary(String filename) 方法加载具有指定库名的动态库。包含本地代码的文件是从本地文件系统从通常获取库文件的地方加载的。此过程的细节取决于实现。从库名到特定文件名的映射是以系统特定的方式完成的。

首先,如果有一个安全管理器,它的 checkLink 方法会以 libname 作为参数调用。 这可能会导致安全异常。 System.loadLibrary(String) 方法是调用此方法的常规且方便的方法。 如果要在类的实现中使用本地方法,标准策略是将本地代码放入库文件(称为 LibFile)中,然后放入静态初始化程序
static { System.loadLibrary("LibFile"); }, 在类声明中。 当类被加载和初始化时,本地方法的必要本地代码实现也将被加载。 如果使用相同的库名称多次调用此方法,则忽略第二次和后续调用。

2.14.3 示例

public class RuntimeDemo {
    @Test
    public void test13() {
        // print when the program starts
        System.out.println("Program starting...");
        
        // load a library that is Windows/System32 folder
        System.out.println("Loading Library...");
        Runtime.getRuntime().loadLibrary("C:/Windows/System32/crypt32.dll");
        System.out.println("Library Loaded.");
    }
}
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.8