JDK-6519418 : (spec) System.nanoTime() needs substantial clarification
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang
  • Affected Version: 6
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2007-01-30
  • Updated: 2017-05-16
  • Resolved: 2011-05-17
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7 Other
7 b15Fixed OpenJDK6Fixed
Description
A DESCRIPTION OF THE REQUEST :
The System.nanoTime() method added in JDK5 needs to be more clearly specified with respect to scope of use, granularity, and relative relationship to System.currentTimeMillis().  I recommend specific javadoc enhancements to both of these methods in these three areas.

First, the scope issue.

In Bug 6380553, Doug Lea writes:  "System.nanoTime [...] returns time relative to an arbitrary but fixed offset".  The JDK6 javadoc says:  "Returns the current value of the most precise available system timer [...] represent[ing] nanoseconds since some fixed but arbitrary time."

What remains unclear is to what extent a developer can rely on the "arbitrary but fixed offset" changing.  It's clear that nanoTime() has a "mutable epoch", but what are the boundaries across which a developer can rely on that epoch NOT changing?  The SDN is sometimes ambiguous on this critical issue.  For example:

http://java.sun.com/docs/books/tutorial/essential/environment/sysmisc.html

suggests that nanoTime() is accurate across the scope of an entire application (which I assume includes all threads, classes and methods).

The javadoc for java.lang.System.nanoTime() refers to a "system timer", which suggests that the nanoTime() epoch would be the same across all applications running on the same "system".  This, of course, begs the question about how the term "system" relates to things like a "JVM", "host machine", "Java Server", etc.

One other meaningful and authorative SDN reference:

http://java.sun.com/developer/technicalArticles/Media/timing/index.html

  states that nanoTime() returns "a time value that is useful only when compared to other return values from that function".  The wording "that function" is unfortunately ambiguous.  Which "function"?  The nanoTime() method, or the method calling nanoTime()?

The times at which this "mutable epoch" may be reset need to be clarified.  Otherwise it's unclear from the API if these values, for example, can be persisted.  They could, technically, still be used by the same "application" on the same "system", but if the epoch is reset on machine boot, then nanoTime() values in a database or on disk become meaningless.

Next: granularity.  This term, and the related terms "accuracy",  "precision" and "resolution" should be clarified as synonymous, or else they should be defined so that nanoTime() and currentTimeMillis() can be clearly understood and differentiated using them.

I'll use the terms loosely here.  Common sense suggests that that nanoTime() would always provide measurements no worse in accuracy than currentTimeMillis().  That is not explicitly stated, but it seems to be the intention.  The extent to which that intention is realized on various platforms seems to cause a lot of confusion in the community.

A few examples for common platforms would be really helpful, so that peoples' expectations can be set properly.  There are many threads on the web (SDN and elsewhere) which illustrate the frustration people have when the actual resolution of nanoTime() is far, far less than expected.

Finally: relative relationship.

Simple math tells us that 1 millisecond = 1 million nanoseconds.  Unfortunately, this forum thread:

http://forum.java.sun.com/thread.jspa?forumID=426&threadID=730138

strongly suggests that there may be "drift" between the two native sources of time used by currentTimeMillis() and nanoTime().  My limited testing supports this theory of relative clock drift, which after only 10 minutes was as much as 12 milliseconds.

If code like the following is not kosher, the API should spell that out:

long start = System.nanoTime();
doSomething();
long millis = (System.nanoTime() - start) / (1000 * 1000);
Date started = new Date(System.currentTimeMillis() - millis);

Otherwise the very concept of a millisecond will be ambiguous in Java.


JUSTIFICATION :
System.nanoTime() solved two important problems.  1) increased timing accuracy on platforms which provide it, and 2) a timing mechanism immune to changes in the platform O/S clock.

Since then a number of issues have arisen which confuse developers about the proper use of nanoTime(), its limitations, and its relationship to currentTimeMillis().  These can be easily corrected in the javadoc.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Enhance the javadoc of the java.lang.System class as follows:

1) clearly document the scope in which a developer may rely on the "epoch" of nanoTime() method:  thread, method, program, JVM, boot, etc.

2) clearly document the terms "granularity", "accuracy", "precision", and "resolution" (others?) as needed, and use them to clarify:

2a) the intended differences between currentTimeMillis() and nanoTime(), and

2b) actual accuracy example for both methods on several of the most common platforms, to help set developer expectations.

3) clearly document the compatibility between the millisecond differentials which are computed using currentTimeMillis() versus nanoTime(), addressing concerns about relative clock drift.
ACTUAL -
The existing JDK5 and JDK6 javadoc is unclear on these issues.

---------- BEGIN SOURCE ----------
public class Test {  // illustrates clock "drift"

    public static void main(String args[]) throws Exception {
        long startN = System.nanoTime();
        long startM = System.currentTimeMillis();
        while (true) {
            Thread.sleep(60000);
            long endM = System.currentTimeMillis();
            long endN = System.nanoTime();
            double diffN = endN - startN;
            diffN /= 1000 * 1000;
            System.out.println(diffN + "\t" + (endM - startM));
        }
    }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Spend a lot of time searching the SDN and elsewhere for everyone's best guess about these issues (optional).  Then make your own best guess.

My recommendations at this point in time (see the Comments section below for updates):

1) Assume the nanoTime() "epoch" is only consistent within a single JVM.  Do not persist values or share them via IPC outside a single JVM.

2) Don't assume you get a lot more timing accuracy with nanoTime().  But switch to it everywhere you're doing "timing" of something in code which should be immune to changes in the PC clock.

3) Don't mix millisecond values from currentTimeMillis() with milliseconds from (nanoTime() / (1000*1000)).  If you do, beware that relative clock drift may skew your results by as much as several milliseconds per minute.

Comments
EVALUATION I'll also add that the drift issue is a mainly a problem on Windows and depends on how the QueryPerformanceCounter API is implemented on that platform. See my blog entry for some related information: http://blogs.sun.com/dholmes/entry/inside_the_hotspot_vm_clocks A laptop with speed-step or other power management speed throttling, using the wrong time source would potentially exhibit the worst drift. On Solaris x86 the test program from the referenced forum thread shows a millis-diff of 9,999 compared to the expected 10,000 as measured by nanoTime(). While on Solaris sparc the difference is only in the nanosecond range. But ignoring hardware and OS problems with maintaining the two different time sources, the fact that currentTimeMillis() is meant to reflect the time-of-day, and the time-of-day may be frequently updated (eg via network-time-protocol) means that you can't reliably compare the passage of time as measured by the two mechanisms.
22-04-2007

EVALUATION David Holmes suggests: - only use nanoTime values to compare against other nanoTime values obtained from within the same VM (this covers his #1 and #3) - resolution (ie the smallest observable update) is dependent on OS and hardware platform but is never worse than currentTimeMillis(). I don't think giving examples is appropriate, but something like "on an operating system with good hardware support a resolution on the order of tens of microseconds is not uncommon" I also think it is worth pointing out that to compare two nanoTime values, long t0 = System.nanoTime(); ... long t1 = System.nanoTime(); one should not do t0 < t1 but instead do t1 - t0 < 0 because of overflow
01-02-2007