深入理解System类及系统启动
Java中有很多的类我们不得不一探究竟,首先我们就来看看这个System类,同时也能了解系统启动时初始化的一些信息,如果还想去底层探究的,就不得不查看Hotspot的源码了,好了,我们开始。
前言
System类是一个JDK工具类,其实也是整个系统的代表。这个类是final类型的,不能继承,不能被实例化。具体的信息可以查看源码。System类包含几个有用的类属性和方法。其中的操作多数和系统相关。其功能主要如下:
- 标准输入输出,如out、in、err,以及设置
- 外部定义的属性和环境变量的访问,如getenv()/setenv()和getProperties()/setProperties();
- 加载文件和类库的方法,如load()和loadLibrary()。
- 一个快速拷贝数组的方法:arraycopy()
- 一些jvm操作,如gc()、runFinalization()、exit(),该部分并未在源码的java doc中提到,可能因为本身不建议主动调用吧。而且这几个方法都仅仅是Runtime.getRuntime()的调用,两者没有区别。
下面是这些功能的一个简图。
关于系统属性
我们在System的类的注释上,可以看到有哪些系统属性,这里简单列一下:
项目 | 说明 |
---|---|
java.version | Java Runtime Environment version |
java.vendor | Java Runtime Environment vendor |
java.vendor.url | Java vendor URL |
java.home | Java installation directory |
java.vm.specification.version | Java Virtual Machine specification version |
java.vm.specification.vendor | Java Virtual Machine specification vendor |
java.vm.specification.name | Java Virtual Machine specification name |
java.vm.version | Java Virtual Machine implementation version |
java.vm.vendor | Java Virtual Machine implementation vendor |
java.vm.name | Java Virtual Machine implementation name |
java.specification.version | Java Runtime Environment specification version |
java.specification.vendor | Java Runtime Environment specification vendor |
java.specification.name | Java Runtime Environment specification name |
java.class.version | Java class format version number |
java.class.path | Java class path |
java.library.path | List of paths to search when loading libraries |
java.io.tmpdir | Default temp file path |
java.compiler | Name of JIT compiler to use |
java.ext.dirs | Path of extension directory or directories |
os.name | Operating system name |
os.arch | Operating system architecture |
os.version | Operating system version |
file.separator | File separator (“/“ on UNIX) |
path.separator | Path separator (“:” on UNIX) |
line.separator | Line separator (“\n” on UNIX) |
user.name | User’s account name |
user.home | User’s home directory |
user.dir | User’s current working directory |
初始化
我们看到类的开头有这样的代码:
private static native void registerNatives();
static {
registerNatives();
}
类中的静态代码块调用了一个native方法registerNatives(),可以猜到该方法应该是一个入口方法,看一**释:通过静态初始化注册native方法,该方法会令vm通过调用initializeSystemClass方法来完成初始化工作。果然如此,那么接下来我们看下initializeSystemClass方法吧:
private static void initializeSystemClass() {
// 初始化props
props = new Properties();
// 这是一个本地方法
initProperties(props);
// 保存并删除系统的一些配置,这里要查看VM的实现了
sun.misc.VM.saveAndRemoveProperties(props);
//获取系统相关的换行符
lineSeparator = props.getProperty("line.separator");
// 主要是系统参数的一些设置,版本号啥的
sun.misc.Version.init();
//分别创建in、out、err的实例对象,并通过setXX0()初始化,查看setXX0()方法可知,这是个native方法,将系统的标准流管理到类内的对象
FileInputStream fdIn = new FileInputStream(FileDescriptor.in);
FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
FileOutputStream fdErr = new FileOutputStream(FileDescriptor.err);
setIn0(new BufferedInputStream(fdIn));
setOut0(new PrintStream(new BufferedOutputStream(fdOut, 128), true));
setErr0(new PrintStream(new BufferedOutputStream(fdErr, 128), true));
//加载zip包以获取java.util.zip.ZipFile这个类,以便之后加载利库使用
loadLibrary("zip");
// 设置平台相关的信号处理
Terminator.setup();
// 初始化sun.misc相关的环境变量
sun.misc.VM.initializeOSEnvironment();
// 主线程不会在同一个线程组中添加相同的线程,我们必须在这里自己实现。看代码就是主线程自己把自己加到了自己的线程组中......
Thread current = Thread.currentThread();
current.getThreadGroup().add(current);
// 该方法就是实例化一个JavaLangAccess
setJavaLangAccess();
// 子系统在初始化的时候可以调用sun.misc.VM.isBooted(),以保证在application类加载器启动前不做任何事。booted()其实就是改了个状态,使isBooted()变为true。
sun.misc.VM.booted();
}
FileDescriptor
文件描述符类的实例用作底层机器特定结构的不透明句柄,表示打开的文件,打开的套接字或字节的另一个源或接收器。文件描述符的主要实际用途是创建一个FileInputStream或FileOutputStream来包含它。应用不应该自己的FileDescriptor类。
Terminator
这个类是设置平台相关的信号处理,我们来看一下这个类。
class Terminator {
// SignalHandler是终止触发事件的管理
private static SignalHandler handler = null;
/* Invocations of setup and teardown are already synchronized
* on the shutdown lock, so no further synchronization is needed here
*/
static void setup() {
if (handler != null) return;
// 默认的接口实现
SignalHandler sh = new SignalHandler() {
// 所以这个接口不需要实现,直接使用即可了
public void handle(Signal sig) {
// 终止号
Shutdown.exit(sig.getNumber() + 0200);
}
};
handler = sh;
try {
// 加入信号处理
Signal.handle(new Signal("INT"), sh);
Signal.handle(new Signal("TERM"), sh);
} catch (IllegalArgumentException e) {
// When -Xrs is specified the user is responsible for
// ensuring that shutdown hooks are run by calling
// System.exit()
}
}
static void teardown() {
/* The current sun.misc.Signal class does not support
* the cancellation of handlers
*/
}
}
这里的SignalHandler是一个接口,这里使用的是匿名类的实现。我们看一下SignalHandler。
package sun.misc;
public abstract interface SignalHandler {
public static final SignalHandler SIG_DFL = new NativeSignalHandler(0L);
public static final SignalHandler SIG_IGN = new NativeSignalHandler(1L);
public abstract void handle(Signal paramSignal);
}
我们看一下Signal类的处理。
package sun.misc;
import java.util.Hashtable;
/**
* 信号类
*/
public final class Signal {
// 信号对应处理器
private static Hashtable handlers = new Hashtable(4);
// 信号编号对信号的映射
private static Hashtable signals = new Hashtable(4);
// 编号
private int number;
// 名称
private String name;
public int getNumber() {
return this.number;
}
public String getName() {
return this.name;
}
public boolean equals(Object paramObject) {
if (this == paramObject) {
return true;
}
if ((paramObject == null) || (!(paramObject instanceof Signal))) {
return false;
}
Signal localSignal = (Signal) paramObject;
return (this.name.equals(localSignal.name)) && (this.number == localSignal.number);
}
public int hashCode() {
return this.number;
}
public String toString() {
return "SIG" + this.name;
}
/**
* 通过name创建signal
* @param name
*/
public Signal(String name) {
this.number = findSignal(name);
this.name = name;
if (this.number < 0)
throw new IllegalArgumentException("Unknown signal: " + name);
}
/**
*
* @param paramSignal
* @param paramSignalHandler
* @return
* @throws IllegalArgumentException
*/
public static synchronized SignalHandler handle(Signal paramSignal, SignalHandler paramSignalHandler)
throws IllegalArgumentException {
// 取得信号编号
long l1 = (paramSignalHandler instanceof NativeSignalHandler) ? ((NativeSignalHandler) paramSignalHandler).getHandler() : 2L;
long l2 = handle0(paramSignal.number, l1);
if (l2 == -1L) {
throw new IllegalArgumentException("Signal already used by VM or OS: " + paramSignal);
}
// 信号编号到信号的映射
signals.put(new Integer(paramSignal.number), paramSignal);
synchronized (handlers) {
SignalHandler localSignalHandler = (SignalHandler) handlers.get(paramSignal);
// 先移除信号,然后重新加入信号映射
handlers.remove(paramSignal);
if (l1 == 2L) {
handlers.put(paramSignal, paramSignalHandler);
}
if (l2 == 0L)
return SignalHandler.SIG_DFL;
if (l2 == 1L)
return SignalHandler.SIG_IGN;
if (l2 == 2L) {
return localSignalHandler;
}
return new NativeSignalHandler(l2);
}
}
/**
* 抛出一个信号,进行处理
* @param paramSignal
* @throws IllegalArgumentException
*/
public static void raise(Signal paramSignal)
throws IllegalArgumentException {
if (handlers.get(paramSignal) == null) {
throw new IllegalArgumentException("Unhandled signal: " + paramSignal);
}
raise0(paramSignal.number);
}
private static void dispatch(int paramInt) {
// 通过信号编号,找到信号
final Signal localSignal = (Signal) signals.get(new Integer(paramInt));
// 找到信号的处理handler
SignalHandler localSignalHandler = (SignalHandler) handlers.get(localSignal);
// 处理信号
Runnable local1 = new Runnable() {
public void run() {
this.val$handler.handle(localSignal);
}
};
// 启动处理线程
if (localSignalHandler != null)
new Thread(local1, localSignal + " handler").start();
}
// 信号的查找是本地方法,没有解
private static native int findSignal(String paramString);
// 信号处理办法
private static native long handle0(int paramInt, long paramLong);
private static native void raise0(int paramInt);
}
这个代码比较简单就不多讲了。
VM.initializeOSEnvironment()方法
public static void initializeOSEnvironment() {
if (!booted)
OSEnvironment.initialize();
}
看一下OSEnvironment的实现。
package sun.misc;
import sun.io.Win32ErrorMode;
public class OSEnvironment {
public static void initialize() {
Win32ErrorMode.initialize();
}
}
这里面好像是win32的系统。具体还是看源码吧,本地方法实在没发看,或许要反编译jvm.dll文件。
setJavaLangAccess
来看一下这个方法。
private static void setJavaLangAccess() {
// Allow privileged classes outside of java.lang
sun.misc.SharedSecrets.setJavaLangAccess(new sun.misc.JavaLangAccess(){
public sun.reflect.ConstantPool getConstantPool(Class klass) {
return klass.getConstantPool();
}
public void setAnnotationType(Class klass, AnnotationType type) {
klass.setAnnotationType(type);
}
public AnnotationType getAnnotationType(Class klass) {
return klass.getAnnotationType();
}
public <E extends Enum<E>>
E[] getEnumConstantsShared(Class<E> klass) {
return klass.getEnumConstantsShared();
}
public void blockedOn(Thread t, Interruptible b) {
t.blockedOn(b);
}
public void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook) {
Shutdown.add(slot, registerShutdownInProgress, hook);
}
public int getStackTraceDepth(Throwable t) {
return t.getStackTraceDepth();
}
public StackTraceElement getStackTraceElement(Throwable t, int i) {
return t.getStackTraceElement(i);
}
public int getStringHash32(String string) {
return string.hash32();
}
});
}
关于这个类的使用,可以看一下我的另一篇博客:SharedSecrets深入理解, 可以看一下里面的JavaLangAccess的实例。
上一篇: centos7 DNS配置
下一篇: yum源的配置