08 September 2017

环境信息

[caoxudong@localhost ~]$ java -version
java version "1.8.0_65"
Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.65-b01, mixed mode)

[caoxudong@localhost ~]$ uname -a
Linux localhost 2.6.32-573.18.1.el6.toav2.x86_64 #1 SMP Sun Jul 17 12:44:29 CST 2016 x86_64 x86_64 x86_64 GNU/Linux

方法说明

方法声明

/**
 * Returns the current time in milliseconds.  Note that
 * while the unit of time of the return value is a millisecond,
 * the granularity of the value depends on the underlying
 * operating system and may be larger.  For example, many
 * operating systems measure time in units of tens of
 * milliseconds.
 *
 * <p> See the description of the class <code>Date</code> for
 * a discussion of slight discrepancies that may arise between
 * "computer time" and coordinated universal time (UTC).
 *
 * @return  the difference, measured in milliseconds, between
 *          the current time and midnight, January 1, 1970 UTC.
 * @see     java.util.Date
 */
public static native long currentTimeMillis();

currentTimeMillis方法用于获取当前的时间戳,单位为毫秒,但获取数据的精度无法确定,依赖于当前操作系统的具体实现。

本地方法的声明,参见jvm.cpp

JVM_LEAF(jlong, JVM_CurrentTimeMillis(JNIEnv *env, jclass ignored))
    JVMWrapper("JVM_CurrentTimeMillis");
    return os::javaTimeMillis();
JVM_END

在各个操作系统下的实现

aix,参见os_aix.cpp

jlong os::javaTimeMillis() {
    timeval time;
    int status = gettimeofday(&time, NULL);
    assert(status != -1, "aix error at gettimeofday()");
    return jlong(time.tv_sec) * 1000 + jlong(time.tv_usec / 1000);
}

bsd,参见os_bsd.cpp

jlong os::javaTimeMillis() {
    timeval time;
    int status = gettimeofday(&time, NULL);
    assert(status != -1, "bsd error");
    return jlong(time.tv_sec) * 1000  +  jlong(time.tv_usec / 1000);
}

linux,参见os_linux.cpp

jlong os::javaTimeMillis() {
    timeval time;
    int status = gettimeofday(&time, NULL);
    assert(status != -1, "linux error");
    return jlong(time.tv_sec) * 1000  +  jlong(time.tv_usec / 1000);
}

solaris,参见os_solaris.cpp

// Must return millis since Jan 1 1970 for JVM_CurrentTimeMillis
jlong os::javaTimeMillis() {
    timeval t;
    if (gettimeofday(&t, NULL) == -1) {
        fatal("os::javaTimeMillis: gettimeofday (%s)", os::strerror(errno));
    }
    return jlong(t.tv_sec) * 1000  +  jlong(t.tv_usec) / 1000;
}

windows,参见os_windows.cpp

jlong os::javaTimeMillis() {
    if (UseFakeTimers) {
        return fake_time++;
    } else {
        FILETIME wt;
        GetSystemTimeAsFileTime(&wt);
        return windows_to_java_time(wt);
    }
}

代码实验

以linux平台为例,测试gettimeofday方法

Test.java

public class Test {

    public static void main(String[] args) {
        long result = 0L;
        for (int i=0; i<10000; i++) {
            result += System.currentTimeMillis();
        }
        System.out.println(result);
    }

}

test.c

#include <stdio.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>

int main(int argc, char* argv[])
{
    for (int i=0; i<10000; i++)
    {
        struct timeval tv;
        gettimeofday (&tv, NULL);
    }
}

运行结果

[caoxudong@localhost ~]$ gcc -std=c99 test.c
[caoxudong@localhost ~]$ javac Test.java
[caoxudong@localhost ~]$ ltrace -c ./a.out
% time     seconds  usecs/call     calls      function
------ ----------- ----------- --------- --------------------
 51.44    2.830242     2830242         1 __libc_start_main
 48.56    2.671857         267     10000 gettimeofday
 0.00     0.000101         101         1
 0.00     0.000094          94         1 SYS_exit_group
------ ----------- ----------- --------- --------------------
100.00    5.502294                 10003 total
[caoxudong@localhost ~]$ ltrace -c java -cp . Test
15046761926697740
% time     seconds  usecs/call     calls      function
------ ----------- ----------- --------- --------------------
 33.99   10.181938      181820        56 __libc_start_main
 33.96   10.174553      181688        56 JLI_Launch
 32.04    9.598370      174515        55 SYS_clone
  0.01    0.002999        2999         1 SYS_exit_group
  0.00    0.000301         100         3
------ ----------- ----------- --------- --------------------
100.00   29.958161                   171 total

这里使用ltrace是因为linux支持VDSO之后,gettimeofday属于快速系统调用,使用strace是看不到执行结果的。

如下所示: gettimeofday.c

#include <sys/time.h>

#ifdef SHARED

# include <dl-vdso.h>
# include <errno.h>

static int
__gettimeofday_syscall (struct timeval *tv, struct timezone *tz)
{
return INLINE_SYSCALL (gettimeofday, 2, tv, tz);
}

void *gettimeofday_ifunc (void) __asm__ ("__gettimeofday");

void *
gettimeofday_ifunc (void)
{
PREPARE_VERSION_KNOWN (linux26, LINUX_2_6);

/* If the vDSO is not available we fall back to syscall.  */
return (_dl_vdso_vsym ("__vdso_gettimeofday", &linux26)
    ?: (void*) (&__gettimeofday_syscall));
}
asm (".type __gettimeofday, %gnu_indirect_function");

libc_ifunc_hidden_def(__gettimeofday)

#else

# include <sysdep.h>
# include <errno.h>

int
__gettimeofday (struct timeval *tv, struct timezone *tz)
{
return INLINE_SYSCALL (gettimeofday, 2, tv, tz);
}
libc_hidden_def (__gettimeofday)

#endif
weak_alias (__gettimeofday, gettimeofday)
libc_hidden_weak (gettimeofday)

这里面值得注意的是,从上面的运行结果看到,运行c程序的时候,调用了10000次gettimeofday系统调用,而运行java程序的时候,却没有调用gettimeofday。原因待查。

参考资料