System

一、基本概述

public final class System {
    private static native void registerNatives();
    static {
        registerNatives();
    }
    private System() {
    }
}

二、属性

三、方法

1、关于System.nanoTime() 与 System.currentTimeMillis()

三个问题:

  • 在mac下发现System.nanoTime()在JDK7和JDK8下输出的值怎么完全不一样?
  • System.nanoTime()的值很奇怪,究竟是怎么算出来的?
  • System.currentTimeMillis()为何不是 System.nanoTime()的 1000000 倍?

1.1、MAC 不同JDK版本下nanoTime实现异同

分析JDK7和JDK8的C语言实现,发现JDK8下多了一个 APPLE 宏下定义的实现,和JDK7及之前的版本的实现是不一样的。不过其他BSD系统是一样的,只是 MacOC 有点不一样

1.2、System.nanoTime()的值很奇怪,究竟是怎么算出来的?

nanoTime其实算出来的是一个相对的时间,相对于系统启动的时候的时间(在linux下JDK7和JDK8的实现都是一样的)

1.3、System.currentTimeMillis()为何不是 System.nanoTime()的 1000000 倍:

currentTimeMillis其实是通过gettimeofday来实现的。System.currentTimeMillis()就是返回的当前时间距离 1970/01/01 08:00:00 的毫秒数

1.4、为什么计算时间从 “1970年1月1日” 开始?

  • Java 起源于 UNIX 系统,而 UNIX 认为 1970 年 1 月 1 日 0 点是时间纪元;

  • 最初计算机操作系统是32 位,而时间也是用 32 位表示,System.out.println(Integer.MAX_VALUE); 2147483647

    因为用32 位来表示时间的最大间隔是68年,而最早出现的UNIX操作系统考虑到计算机产生的年代和应用的时限综合取了1970年1月1日作为UNIX TIME的纪元时间,Integer在JAVA内用32位表示,因此32位能表示的最大值是2147483647,另外1年 365天的总秒数是31536000,2147483647/31536000 = 68.1,也就是说32 位能表示的最长时间是68年,而实际上到2038年01月19日03时14分07秒,便会到达最大时间;这就是 2038 问题

  • 至于时间回归的现象相信随着64 为操作系统 的产生逐渐得到解决,因为用64位操作系统可以表示到“292,277,026,596年12月4日15时30分08秒”;

  • System.out.println(new Date(0)),打印出来的时间是8点而非0点存在系统时间和本地时间的问题,其实系统时间依然是0点,不过跟你所在时区有关。

2、System.gc方法

2.1、方法实现

public static void gc() {
	Runtime.getRuntime().gc();
}
/*
运行垃圾收集器。
调用此方法表明,java虚拟机扩展。努力回收未使用的对象,以便内存可以快速复用,当控制从方法调用返回的时候,虚拟机尽力回收被丢弃的对象
*/
public native void gc();

2.2、作用

  • 做一次full gc
  • 执行后会暂停整个进程。
  • System.gc我们可以禁掉,使用-XX:+DisableExplicitGC,其实一般在cms gc下我们通过-XX:+ExplicitGCInvokesConcurrent也可以做稍微高效一点的gc,也就是并行gc。
  • 最常见的场景是RMI/NIO下的堆外内存分配等;

3、getProperties 与 getEnv

3.1、getProperties

Java平台使用Properties对象来提供关于本地系统和配置的信息,我们称之为系统属性。

系统属性包括当前用户、Java运行时的当前版本和文件路径名分隔符等信息。

可以使用System.getProperty("log_dir")读取属性log_dir的值。还可以使用了默认值参数,如果属性不存在,getProperty返回/tmp/log

String log_dir = System.getProperty("log_dir","/tmp/log");
// 如果在运行时需要更新,可以调用如下方法:
System.setProperty("log_dir", "/tmp/log");

也可以使用命令行的形式设置Property,这样通过System.getProperty 返回的是个字符串

java -jar jarName -DpropertyName=value

所有系统的Properties属性:

KeyDescription of Associated Value
java.versionJava Runtime Environment version
java.vendorJava Runtime Environment vendor
java.vendor.urlJava vendor URL
java.homeJava installation directory
java.vm.specification.versionJava Virtual Machine specification version
java.vm.specification.vendorJava Virtual Machine specification vendor
java.vm.specification.nameJava Virtual Machine specification name
java.vm.versionJava Virtual Machine implementation version
java.vm.vendorJava Virtual Machine implementation vendor
java.vm.nameJava Virtual Machine implementation name
java.specification.versionJava Runtime Environment specification version
java.specification.vendorJava Runtime Environment specification vendor
java.specification.nameJava Runtime Environment specification name
java.class.versionJava class format version number
java.class.pathJava class path
java.library.pathList of paths to search when loading libraries
java.io.tmpdirDefault temp file path
java.compilerName of JIT compiler to use
java.ext.dirsPath of extension directory or directoriesDeprecated. This property, and the mechanism which implements it, may be removed in a future release.
os.nameOperating system name
os.archOperating system architecture
os.versionOperating system version
file.separatorFile separator ("/" on UNIX)
path.separatorPath separator (":" on UNIX)
line.separatorLine separator (" " on UNIX)
user.nameUser's account name
user.homeUser's home directory
user.dirUser's current working directory

3.2、getEnv

环境变量像Properties一样,是一个键值对,操作系统使用环境变量来允许将配置信息传递到应用程序;设置环境变量的方法因操作系统的不同而不同。例如,在Windows中,我们使用来自控制面板的系统实用程序,而在Unix中,我们使用shell脚本

getEnv 返回的是一个只读的Map,如果向Map中添加值的话,会报UnsupportedOperationException;

如果需要向环境变量添加新变量,要在Java中创建一个新进程,可以使用ProcessBuilder类,它有一个名为environment的方法。这个方法返回一个映射,但是这次映射不是只读的,这意味着我们可以向它添加元素

List<String> command = new ArrayList<>();
command.add("/bin/ls");
command.add("/root");

ProcessBuilder pb = new ProcessBuilder(command);
Map<String, String> env = pb.environment();
env.put("log_dir", "/tmp/log");
Process process = pb.start();

4、load 与 loadLibrary

JDK 提供给了我们两个方法用于载入库文件,一个是System.load(String filename)方法,另一个是System.loadLibrary(String libname)方法,他们的区别主要如下:

  • 加载的路径不同:

    • System.load(String filename) 是从作为动态库的本地文件系统中以指定的文件名加载代码文件,文件名参数必须是完整的路径名且带文件后缀
    • System.loadLibrary(String libname) 是加载由libname参数指定的系统库(系统库指的是java.library.path,可以通过 System.getProperty(String key) 方法查看 java.library.path 指向的目录内容),将库名映射到实际系统库的方法取决于系统实现,譬如在 Android 平台系统会自动去系统目录、应用 lib 目录下去找 libname 参数拼接了 lib 前缀的库文件;
  • 是否自动加载库的依赖库:

    譬如libA.so 和 libB.so 有依赖关系

    • 如果选择 System.load("/sdcard/path/libA.so"),即使 libB.so 也放在 /sdcard/path/ 路径下,load 方法还是会因为找不到依赖的 libB.so 文件而失败,因为虚拟机在载入 libA.so 的时候发现它依赖于 libB.so,那么会先去 java.library.path 下载入 libB.so,而 libB.so 并不位于 java.library.path 下,所以会报错;
    • 使用 System.loadLibrary("A"),然后把 libA.so 和 libB.so 都放在 java.library.path 下即可

参考文章

别废话,拿你代码给我看。
原文地址:https://www.cnblogs.com/lvxueyang/p/15073699.html