欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

深入理解System类及系统启动

程序员文章站 2022-05-28 10:21:02
...

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类及系统启动

关于系统属性

我们在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的实例。

相关标签: System

上一篇: centos7 DNS配置

下一篇: yum源的配置