Deep Dive into System.currentTimeMillis(): Implementation, Performance, and Time Sources across Platforms
This article investigates the implementation of Java's System.currentTimeMillis() on Windows, macOS, and Linux, measures its execution speed, explores the underlying native methods and OS time‑source mechanisms such as TSC, HPET, and RTC, and discusses performance implications and best practices for high‑frequency timestamp retrieval.
The article begins by recalling a previous discussion on date‑time handling in Java and MySQL, then introduces an unexpected performance phenomenon when repeatedly calling System.currentTimeMillis() one hundred million times and measuring the average execution time.
Experimental results on three platforms (Windows 10, macOS High Sierra, and a Linux server) show a two‑order‑magnitude difference in execution speed, prompting a deeper investigation.
2 Deep Exploration of System.currentTimeMillis()
The method is a native call; its JVM implementation forwards to os::javaTimeMillis(). On Windows, the code in os_windows.cpp ultimately calls GetSystemTimeAsFilePath, converts the FILETIME (100 ns units since 1601‑01‑01) to Unix milliseconds, and returns the result. The article includes the relevant C++ snippets:
JVM_LEAF(jlong, JVM_CurrentTimeMillis(JNIEnv *env, jclass ignored))
JVMWrapper("JVM_CurrentTimeMillis");
return os::javaTimeMillis();
JVM_END jlong os::javaTimeMillis() {
if (UseFakeTimers) {
return fake_time++;
} else {
FILETIME wt;
GetSystemTimeAsFileTime(&wt);
return windows_to_java_time(wt);
}
}
jlong windows_to_java_time(FILETIME wt) {
jlong a = jlong_from(wt.dwHighDateTime, wt.dwLowDateTime);
return (a - offset()) / 10000;
}
jlong offset() { return _offset; }
static jlong _offset = 116444736000000000;The Windows implementation reads two 32‑bit fields from a fixed memory area, performs a simple arithmetic conversion, and thus is extremely fast (≈4.77 ns per call). The article also shows the GetSystemTimeAsFileTime documentation and explains the 1601 epoch conversion.
On Linux, os::javaTimeMillis() calls gettimeofday():
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;
}The gettimeofday implementation resides in the vDSO ( __vdso_gettimeofday) and ultimately uses do_realtime(), which reads a high‑frequency timer (TSC, HPET, or PVCLOCK) via vgetsns() and converts cycles to nanoseconds using the clocksource structure (fields mult and shift).
int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz) {
long ret = VCLOCK_NONE;
if (likely(tv != NULL)) {
ret = do_realtime((struct timespec *)tv);
tv->tv_usec /= 1000;
}
if (ret == VCLOCK_NONE)
return vdso_fallback_gtod(tv, tz);
return 0;
} static inline u64 vgetsns(int *mode) {
long v;
cycles_t cycles;
if (gtod->clock.vclock_mode == VCLOCK_TSC)
cycles = vread_tsc();
else if (gtod->clock.vclock_mode == VCLOCK_HPET)
cycles = vread_hpet();
else if (gtod->clock.vclock_mode == VCLOCK_PVCLOCK)
cycles = vread_pvclock(mode);
else
return 0;
v = (cycles - gtod->clock.cycle_last) & gtod->clock.mask;
return v * gtod->clock.mult;
}The Linux clocksource structure defines how cycles are transformed into nanoseconds, with important fields rating, mult, and shift. Ratings indicate suitability: TSC (rating 300), HPET (rating 250), ACPI PM (rating 200). The article lists these ratings and shows a table of available clocksources on the test machine.
Further sections describe common hardware timers (RTC, TSC, PIT, HPET, etc.), their characteristics, and how the kernel abstracts them. The article explains the conversion formula time = cycles * mult >> shift and provides the inline helper clocksource_cyc2ns.
static inline s64 clocksource_cyc2ns(cycle_t cycles, u32 mult, u32 shift) {
return ((u64)cycles * mult) >> shift;
}Performance comparisons reveal that Windows' fixed‑memory approach is fastest, while Linux's additional high‑frequency timer reads add overhead. Multi‑threaded experiments show that using HPET can cause linear slowdown due to contention, whereas TSC scales better. To mitigate high‑frequency overhead, the article suggests caching timestamps in a dedicated thread, switching to clock_gettime via JNI, or using System.nanoTime() for finer granularity. 4 Summary The middle part of the series dissected System.currentTimeMillis() , exposing platform‑specific implementations and performance differences, and introduced the underlying hardware timers and kernel abstractions that affect timestamp retrieval.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Sohu Tech Products
A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
