- 1️⃣ 概念
- 2️⃣ 优势和缺点
- 3️⃣ 使用
-
- 3.1 Runtime 类常用方法
- 3.2 使用技巧
- 4️⃣ 应用场景
- 5️⃣ 业务场景实战
-
- 5.1 场景一:执行外部脚本
- 5.2 场景二:动态加载类
- * 总结
1️⃣ 概念
Java Runtime 类是 Java 标准库中的关键类之一。它提供了对当前Java虚拟机(JVM)实例的访问和控制,允许程序动态地修改和管理运行时环境。
Java Runtime 是Java虚拟机(JVM)的一个实例,代表了正在执行Java应用程序的运行时环境。Runtime 类封装了访问底层系统和控制JVM行为的方法,使得程序能够与运行时环境进行交互。
2️⃣ 优势和缺点
优点:
- 控制整个JVM:Runtime 类提供了许多方法来管理JVM的行为,如内存管理、垃圾回收等。这使得开发人员可以根据自己的需求调整JVM的配置,并最大限度地使用系统资源;
- 外部交互能力:通过 exec 方法,Runtime 类可以执行外部命令,允许程序与操作系统进行交互。这样,Java程序就能够调用其他系统工具或执行外部脚本;
- 动态类加载:Runtime 类支持在运行时动态加载和卸载类,从而提供了更大的灵活性和扩展性。
缺点:
- 安全性问题:某些 Runtime 类的方法可能存在安全风险,尤其是在处理用户输入时。开发人员应谨慎使用这些方法,并进行适当的输入验证和安全性检查;
- 平台依赖性:Runtime 类涉及底层系统资源和行为,因此其表现可能依赖于特定的操作系统和JVM实现。在不同平台上的行为可能会有所差异。
3️⃣ 使用
3.1 Runtime 类常用方法
下表列出了 Java Runtime 类的全部操作方法API,并简要描述了每个方法的作用。开发人员可以根据自己的需求选择适当的方法来管理和控制Java运行时环境:
方法 | 说明 |
---|---|
Runtime getRuntime() |
返回与当前 Java 应用程序相关的运行时对象 |
Process exec(String command) |
在单独的进程中执行指定的字符串命令。该方法返回一个代表进程的Process 对象,可以通过该对象获取进程的输入流、输出流和出错流,并与进程进行交互 |
gc() |
运行垃圾回收器。调用该方法可以尽力触发垃圾回收器的执行,以回收不再使用的对象占据的内存空间 |
addShutdownHook(Thread hook) |
注册在JVM关闭时执行的钩子程序。当Java虚拟机即将关闭时,会先执行注册的所有钩子程序,然后终止虚拟机 |
boolean removeShutdownHook(Thread hook) |
注销先前通过addShutdownHook 方法注册的钩子。该方法返回一个布尔值,表示注销是否成功。注意,只能在钩子尚未运行并且没有被虚拟机触发关闭序列的情况下才能成功注销钩子。否则,调用将不起作用,并返回false |
load(String filename) |
加载关联的本地库。根据给定的文件名加载与当前平台兼容的动态链接库或共享对象文件 |
loadLibrary(String libname) |
加载具有指定名称的本地库。加载与系统属性java.library.path 中定义的路径和给定的库名相对应的本地库。通常,库名由平台相关的前缀和后缀组成,如libexample.so (UNIX)和 example.dll (Windows) |
long freeMemory() |
返回Java虚拟机中的可用内存量(以字节为单位)。这个值会随着程序运行而不断变化,可以用来监视内存的使用情况 |
long maxMemory() |
返回Java虚拟机试图使用的最大内存量(以字节为单位)。这个值通常取决于底层操作系统和Java虚拟机的限制 |
long totalMemory() |
返回Java虚拟机中的堆的当前大小(以字节为单位)。堆是用于存储Java对象和数组的区域,这个值通常是JVM启动时分配的初始堆大小,但在程序运行时也会动态增长 |
runFinalization() |
强制终止正在等待的所有用户定义的对象的finalize() 方法。当垃圾回收器确定某个对象需要被回收时,会先执行其finalize() 方法。但是,在执行回收之前,可以通过runFinalization() 强制执行处于等待状态的所有finalize() 方法 |
int availableProcessors() |
返回可用的处理器数目。这个值表示当前系统上可用的处理器核心数量,用于评估处理能力和并行性 |
traceInstructions(boolean on) |
启用或禁用指令跟踪。当参数on 为true ,JVM将输出正在执行的每条指令。这个方法主要用于调试目的,可能影响程序性能和日志文件大小 |
traceMethodCalls(boolean on) |
启用或禁用方法调用跟踪。当参数on 为true ,JVM将输出方法调用的信息,包括方法入口和出口。这个方法主要用于调试目的,可能影响程序性能和日志文件大小 |
exit(int status) |
终止当前正在运行的Java虚拟机。参数status 表示退出状态码,通常非零值表示异常终止 |
halt(int status) |
强制终止当前虚拟机,非标准化暴力关机。这个方法直接终止虚拟机,不会正常释放资源。参数status 表示终止状态码,通常非零值表示异常终止 |
InputStream getLocalizedInputStream(InputStream in) |
获取本地化标准输入流。这个方法返回一个用于读取控制台输入的输入流。根据操作系统的不同,可能会返回不同的输入流 |
OutputStream getLocalizedOutputStream(OutputStream out) |
获取本地化标准输出流。这个方法返回一个用于向控制台输出数据的输出流。根据操作系统的不同,可能会返回不同的输出流 |
在学习面向对象概念时曾经强调过一个概念,一个类中至少会存在一个构造方法, 如果本类没有定义任何构造方法,那么会自动生成一个无参的构造方法。 但是当用户打开 Runtime
类时会发现一个问题,在这个类中并没有构造方法的定义说明,可是这个类的构造方法却是真实存在的,因为其在声明时对构造方法进行了封装 所以Runtime
类是一个典型的单例设计模式。
单例设计模式所属的类一定会提供一个 static
型的方法,用于取得本类的实例化对象,所以在 Runtime
类中也提供了一个 getRuntime()
方法用于取得本类实例化对象:
public static Runtime getRuntime();
而在Runtime 类有一个较重要的方法: public void gc()
,用于运行垃圾收集器,释放垃圾空间,即调用此方法后内存中所产生的垃圾空间将被释放。
// 范例 : 观察gc() 使用前后的内存占用率
package com.xiaoshan.demo;
public class TestDemo {
public static void main(String[] args) throws Exception {
Runtime run= Runtime.getRuntime(); // 取得Runtime 类的实例化对象
String str = "";
for (int x=0; x<2000; x++){
str += x; //产生大量垃圾
}
System.out.println("【垃圾处理前内存量】MAX = " + run.maxMemory());
System.out.println("【垃圾处理前内存量】TOTAL = " + run.totalMemory());
System.out.println("【垃圾处理前内存量】FREE = " + run.freeMemory());
run.gc(); //释放垃圾空间
System.out.println("【垃圾处理后内存量】MAX = " + run.maxMemory());
System.out.println("【垃圾处理后内存量】TOTAL = " + run.totalMemory());
System.out.println("【垃圾处理后内存量】FREE = " + run.freeMemory());
}
}
程序执行结果:
【垃圾处理前内存量】MAX = 259522560
【垃圾处理前内存量】TOTAL = 16252928
【垃圾处理前内存量】FREE = 11908336 (空闲空间减少)
【垃圾处理后内存量】MAX = 259522560
【垃圾处理后内存量】TOTAL = 16318464
【垃圾处理后内存量】FREE = 15731872 (空闲空间释放)
除了上述方法之外,下面是一个使用 Runtime 类的案例程序,逐行解释了每个方法的使用和功能:
import java.io.IOException;
public class RuntimeExample {
public static void main(String[] args) throws IOException {
// getRuntime - 返回与当前 Java 应用程序相关的运行时对象
Runtime runtime = Runtime.getRuntime();
// exec - 在单独的进程中执行指定的字符串命令
Process process = runtime.exec("echo Hello World");
// gc - 运行垃圾回收器
runtime.gc();
// load - 加载关联的本地库
runtime.load("/path/to/library.so");
// loadLibrary - 加载具有指定名称的本地库
runtime.loadLibrary("mylibrary");
// freeMemory - 返回Java虚拟机中的可用的内存量
long freeMemory = runtime.freeMemory();
System.out.println("Free memory: " + freeMemory + " bytes");
// maxMemory - 返回Java虚拟机试图使用的最大内存量
long maxMemory = runtime.maxMemory();
System.out.println("Max memory: " + maxMemory + " bytes");
// totalMemory - 返回Java虚拟机中的堆的当前大小
long totalMemory = runtime.totalMemory();
System.out.println("Total memory: " + totalMemory + " bytes");
// runFinalization - 强制终止正在等待的所有的用户定义的对象的finalize方法
runtime.runFinalization();
// traceInstructions - 启用/禁用指令跟踪
runtime.traceInstructions(true);
// traceMethodCalls - 启用/禁用方法调用跟踪
runtime.traceMethodCalls(true);
// availableProcessors - 返回可用的处理器数目
int numProcessors = runtime.availableProcessors();
System.out.println("Available processors: " + numProcessors);
// addShutdownHook - 注册在JVM关闭时执行的钩子程序
Thread thread = new Thread() {
public void run() {
System.out.println("Shutting down JVM...");
}
};
runtime.addShutdownHook(thread);
// exit - 终止当前正在运行的Java虚拟机
runtime.exit(0);
// removeShutdownHook - 注销先前通过addShutdownHook方法注册的钩子
runtime.removeShutdownHook(thread);
// halt - 强制终止当前虚拟机,非标准化暴力关机,不会正常释放资源
runtime.halt(1);
}
}
通过以上示例,我们可以了解每个 Runtime 类的方法和功能。请注意,有些方法涉及到I/O操作或系统资源,需要显式处理异常或适当的关闭流。另外,本例中的部分方法可能会导致程序终止或环境变化,请谨慎使用,并根据自己的需求进行适当的调整和优化。
特别的对于 runtime.load("/path/to/library.so");
与 runtime.loadLibrary("mylibrary");
这两个方法在执行之前,先确保已经在正确对应位置创建好了本地库文件,否则调试时可能出现下列异常:
java.lang.UnsatisfiedLinkError: Expecting an absolute path of the library: /path/to/library.so
或
java.lang.UnsatisfiedLinkError: no mylibrary in java.library.path
程序运行结果如下:
Free memory: 126201960 bytes
Max memory: 1866465280 bytes
Total memory: 126877696 bytes
Available processors: 12
Shutting down JVM...
3.2 使用技巧
- 要注意安全性问题,特别是在处理用户输入时要做好验证和过滤;
- 避免滥用 Runtime 类的方法,合理利用其功能并进行适当的优化以提高性能;
- 避免频繁创建 Runtime 类的实例,可以通过 getRuntime 方法获取单例实例,以减少资源消耗;
- 在调用 exec 执行外部命令时,注意处理输入、输出流以避免阻塞;
- 使用 addShutdownHook 方法注册终止钩子,确保程序退出前能够释放资源。
4️⃣ 应用场景
Java Runtime 在需要与底层操作系统进行交互的情况下常被使用:
- 执行外部命令,如运行系统命令行工具或执行脚本;
- 动态加载本机库文件,与本地代码进行交互;
- 监听JVM关闭事件,执行清理活动或保存状态等操作。
5️⃣ 业务场景实战
5.1 场景一:执行外部脚本
下面是一个演示如何使用 Runtime
类来执行外部脚本的 Java 实例程序:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class ExecuteScriptExample {
public static void main(String[] args) {
Runtime runtime = Runtime.getRuntime();
try {
// 使用 runtime.exec() 方法执行外部脚本
Process process = runtime.exec("python script.py");
// 获取外部脚本的输入流,并创建 BufferedReader 对象用于读取输出结果
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
// 处理外部脚本输出的每一行
System.out.println(line);
}
// 等待外部进程结束并获取退出值
int exitCode = process.waitFor();
if (exitCode == 0) {
// 外部脚本执行成功
System.out.println("External script executed successfully.");
} else {
// 外部脚本执行失败
System.err.println("External script execution failed. Exit code: " + exitCode);
}
} catch (IOException | InterruptedException e) {
// 处理异常
e.printStackTrace();
}
}
}
上面代码首先通过 Runtime.getRuntime()
方法获取运行时对象,这是一个表示当前 Java 应用程序相关的运行时环境的单例对象。然后使用 runtime.exec()
方法执行外部脚本命令,例如 "python script.py"
。
通过Process.getInputStream()
方法获取外部进程的输入流,并使用 InputStreamReader
和 BufferedReader
对象读取脚本的输出结果。在循环中,处理并打印每一行输出。
最后,通过 process.waitFor()
方法等待外部进程结束,并获取退出值。根据约定,退出值为 0 表示成功执行,而非零值表示出现错误。根据退出值,可以确定外部脚本是成功执行还是失败。
这个实例展示了如何在 Java 中使用 Runtime
类来执行外部脚本,并处理输出结果和异常情况。注意,使用 runtime.exec()
执行外部命令需要谨慎处理,特别是针对从用户输入或不受信任的源代码中派生的命令。建议对脚本及其参数进行适当的验证和限制,以确保安全性。
5.2 场景二:动态加载类
下面是一个演示如何使用 Runtime
类结合反射机制来动态加载类的Java实例程序:
import java.lang.reflect.Method;
public class DynamicClassLoadingExample {
public static void main(String[] args) {
try {
// 创建Runtime对象
Runtime runtime = Runtime.getRuntime();
// 加载需要动态加载的类的全限定名
String className = "com.example.MyClass";
// 使用ClassLoader动态加载类
Class<?> dynamicClass = runtime.getClass().getClassLoader().loadClass(className);
// 创建类的实例
Object instance = dynamicClass.newInstance();
// 调用类中的方法
Method method = dynamicClass.getDeclaredMethod("doSomething");
method.invoke(instance);
} catch (ClassNotFoundException e) {
// 处理类未找到异常
e.printStackTrace();
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
// 处理其他异常
e.printStackTrace();
}
}
}
上面代码首先创建了一个 Runtime
对象,用于表示当前Java应用程序相关的运行时环境。
然后,通过获取 runtime
对象的类加载器(runtime.getClass().getClassLoader()
)和指定的类名,使用 loadClass()
方法从类加载器中动态加载需要的类。
接下来,通过反射实例化被加载的类,使用 dynamicClass.newInstance()
创建了一个类的实例,并使用反射获取被加载类中的方法(例如 doSomething()
),并使用 method.invoke(instance)
调用该方法。
这个实例演示了如何使用 Runtime
类动态加载类,并调用其中的方法。需要注意,动态加载类在某些场景下是有用的,但也应谨慎使用。合理地处理异常情况以及对类名、方法等进行适当的验证和限制是非常重要的。
* 总结
Java Runtime 类作为Java虚拟机的实例,对于动态地控制运行时环境至关重要。通过提供各种功能和方法,例如执行外部命令、管理内存和垃圾回收,以及动态加载和卸载类,Runtime 类使得Java程序能够更加灵活地与底层系统进行交互。
然而,开发人员需要注意安全性、性能优化及平台依赖性等方面,以确保代码的可靠性和效率。熟练使用 Runtime 类,并根据具体需求进行合理的优化,将有助于实现更强大和高效的Java应用程序。
[ ]nbsp_nbsp 1