System currenttimemillis java что возвращает
Перейти к содержимому

System currenttimemillis java что возвращает

  • автор:

Class System

For simple stand-alone Java applications, a typical way to write a line of output data is:

See the println methods in class PrintStream .

Typically this stream corresponds to display output or another output destination specified by the host environment or user. By convention, this output stream is used to display error messages or other information that should come to the immediate attention of a user even if the principal output stream, the value of the variable out , has been redirected to a file or other destination that is typically not continuously monitored. The encoding used in the conversion from characters to bytes is equivalent to Console.charset() if the Console exists, stderr.encoding otherwise.

Method Details

setIn

setOut

setErr

console

inheritedChannel

In addition to the network-oriented channels described in inheritedChannel , this method may return other kinds of channels in the future.

setSecurityManager

Otherwise, the argument is established as the current security manager. If the argument is null and no security manager has been established, then no action is taken and the method simply returns.

getSecurityManager

currentTimeMillis

See the description of the class Date for a discussion of slight discrepancies that may arise between «computer time» and coordinated universal time (UTC).

nanoTime

This method provides nanosecond precision, but not necessarily nanosecond resolution (that is, how frequently the value changes) — no guarantees are made except that the resolution is at least as good as that of currentTimeMillis() .

Differences in successive calls that span greater than approximately 292 years (2 63 nanoseconds) will not correctly compute elapsed time due to numerical overflow.

The values returned by this method become meaningful only when the difference between two such values, obtained within the same instance of a Java virtual machine, is computed.

For example, to measure how long some code takes to execute:

To compare elapsed time against a timeout, use instead of because of the possibility of numerical overflow.

arraycopy

If the src and dest arguments refer to the same array object, then the copying is performed as if the components at positions srcPos through srcPos+length-1 were first copied to a temporary array with length components and then the contents of the temporary array were copied into positions destPos through destPos+length-1 of the destination array.

If dest is null , then a NullPointerException is thrown.

If src is null , then a NullPointerException is thrown and the destination array is not modified.

  • The src argument refers to an object that is not an array.
  • The dest argument refers to an object that is not an array.
  • The src argument and dest argument refer to arrays whose component types are different primitive types.
  • The src argument refers to an array with a primitive component type and the dest argument refers to an array with a reference component type.
  • The src argument refers to an array with a reference component type and the dest argument refers to an array with a primitive component type.
  • The srcPos argument is negative.
  • The destPos argument is negative.
  • The length argument is negative.
  • srcPos+length is greater than src.length , the length of the source array.
  • destPos+length is greater than dest.length , the length of the destination array.

Otherwise, if any actual component of the source array from position srcPos through srcPos+length-1 cannot be converted to the component type of the destination array by assignment conversion, an ArrayStoreException is thrown. In this case, let k be the smallest nonnegative integer less than length such that src[srcPos+ k ] cannot be converted to the component type of the destination array; when the exception is thrown, source array components from positions srcPos through srcPos+ k -1 will already have been copied to destination array positions destPos through destPos+ k -1 and no other positions of the destination array will have been modified. (Because of the restrictions already itemized, this paragraph effectively applies only to the situation where both arrays have component types that are reference types.)

identityHashCode

getProperties

The current set of system properties for use by the getProperty(String) method is returned as a Properties object. If there is no current set of system properties, a set of system properties is first created and initialized. This set of system properties includes a value for each of the following keys unless the description of the associated value indicates that the value is optional.

Shows property keys and associated values

Key Description of Associated Value
java.version Java Runtime Environment version, which may be interpreted as a Runtime.Version
java.version.date Java Runtime Environment version date, in ISO-8601 YYYY-MM-DD format, which may be interpreted as a LocalDate
java.vendor Java Runtime Environment vendor
java.vendor.url Java vendor URL
java.vendor.version Java vendor version (optional)
java.home Java installation directory
java.vm.specification.version Java Virtual Machine specification version, whose value is the feature element of the runtime 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 which may be interpreted as a Runtime.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, whose value is the feature element of the runtime version
java.specification.maintenance.version Java Runtime Environment specification maintenance version, may be interpreted as a positive integer (optional, see below)
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 (refer to ClassLoader.getSystemClassLoader() for details)
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
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
native.encoding Character encoding name derived from the host environment and/or the user’s settings. Setting this system property has no effect.
stdout.encoding Character encoding name for System.out . The Java runtime can be started with the system property set to UTF-8 , starting it with the property set to another value leads to undefined behavior.
stderr.encoding Character encoding name for System.err . The Java runtime can be started with the system property set to UTF-8 , starting it with the property set to another value leads to undefined behavior.

The java.specification.maintenance.version property is defined if the specification implemented by this runtime at the time of its construction had undergone a maintenance release. When defined, its value identifies that maintenance release. To indicate the first maintenance release this property will have the value «1» , to indicate the second maintenance release this property will have the value «2» , and so on.

Multiple paths in a system property value are separated by the path separator character of the platform.

Note that even if the security manager does not permit the getProperties operation, it may choose to permit the getProperty(String) operation.

lineSeparator

On UNIX systems, it returns «\n» ; on Microsoft Windows systems it returns «\r\n» .

Сколько времени выполнялась программа (Java)

Как не пытался конвертировать и где только не гуглил,выход так и не нашел.

Мне нужно преобразовать полученное значение в секунды. Т.е. вывести на экран время выполнения программы.

Marat Zimnurov's user avatar

System.currentTimeMillis() — возвращает количество миллисекунд прошедших с полуночи 1 января 1970 года, это называется UNIX-время.

Чтобы посчитать сколько времени выполнялся какой-то кусок кода, нужно посчитать разницу, т.е. что-то типа:

Так же часто код выполняется очень быстро, тогда надо заменить за сколько выполнится большое количество повторений этого кода.

Или не всегда за стабильное время, в этом случае надо провести несколько замеров и посчитать среднее арифметическое.

System currenttimemillis java что возвращает

Before java 8 the compiler used to take it from the java.util package. But later since the use of date and time became important in every software and android application, java developed the date class after java 8. Date and time class via java.util package has been duplicated now. Java uses date and time class after the release of version java 8 to store the date and time. Now java usually stores Date in a typical fashion such that the number of milliseconds passed since 1 Jan 1970 in a long data type. Since it stores milliseconds the accuracy of the exact time increases.

The package view of the method is as follows:

Syntax: Getting milliseconds

Note: This return the number of milliseconds passed since 1970 as 00:00 1 January 1970 is considered as epoch time. Similarly, we can find out years, months, hours and rest of data from milliseconds.

(System.currentTimeMillis()) / 1000) Returns Seconds passed

(System.currentTimeMillis()) / 1000 / 60) Returns Minutes passed

(System.currentTimeMillis()) / 1000 / 60 / 60); Returns Hours passed

(System.currentTimeMillis()) / 1000 / 60 / 60 / 24); Returns days passed

(System.currentTimeMillis()) / 1000 / 60 / 60 / 24 / 365); Returns Years passed

Example 1: Here all outputs are obtained from the epoch time set which is 1 January 1970. We can easily find seconds now if we divide the millisecond by 1000 we will get the number of seconds passed since 1 Jan 1970.

The slow currentTimeMillis()

Today we’ll look at one of the most basic and the most often used methods from the Java library: System.currentTimeMillis() .

This method reports current time with the millisecond accuracy. One may think that, because of this, the performance of this method is irrelevant. Who cares if obtaining current time takes 0.1 or 0.2 ms if the measured interval is 100 or 500 milliseconds long? There are, however, cases when we might still want to invoke this method frequently. Here are the examples:

Detecting and reporting abnormally long execution times. For instance, we can measure the time it takes to execute an HTTP request. In most cases (we hope) it takes below one milliseconds, which will report as zero if we use this method, but we want to be alarmed if the measured time is abnormally long (e.g. exceeds 100 ms). In this case we’ll time every request, and there may be hundreds of thousands, or even millions, of them per second.

Obtaining timestamps to be associated with some objects, for instance, with cached data – to arrange time-based eviction from a cache.

Measuring the duration of some long, but frequently initiated asynchronous processes, such as requests to remote servers.

Time-stamping some real-world events. For instance, a trading system may wish to record timestamps of incoming orders and performed deals.

In short, despite rather low accuracy of this method, there are cases when it can be called very often, so a very valid question arises: what is the performance of this method?

Measuring speed

The way to test the performance of currentTimeMillis() is straightforward: we call it many times, while making sure that it isn’t optimised out altogether:

Running it on Windows (my notebook, which is Windows 10, Core i7-6820HQ @2.7GHz), using Java 1.8.0_92, we get:

This is very good result. When time is reported in 3.8 ns, we can put time request instructions just about anywhere. We can measure time of any operation and attach timestamp to any resource. It is virtually free. We can query time 260 million times per second.

Let’s run it on Linux. The one I use for testing is of RHEL flavour with a kernel version of 3.17.4, and it runs on a dual Xeon® CPU E5-2620 v3 @ 2.40GHz.

We had to reduce the iteration count by a couple of zeroes (make it one million), because the test runs two hundred times longer. The average time to query time in Linux is 640 ns, which is more than half a microsecond. We can only execute this call 1.5 million times per second.

This is really shocking, and it means for us that we must be careful with our use of currentTimeMillis() on Linux. While still applicable for measuring time of sequential long operations, this method can’t really be used for the tasks listed above.

Why is this and what can be done?

Windows version

The currentTimeMillis() is a native function. Its code can be found in OpenJDK distribution, where it is linked to JVM_CurrentTimeMillis (file hotspot/src/share/vm/prims/jvm.cpp ), which eventually ends up at os::javaTimeMillis() . This call is OS-dependent.

The Windows version of this code ( hotspot/src/os/windows/vm/jvm.cpp/os_windows.cpp ) looks like this:

Java time is based on GetSystemTimeAsFileTime() , which returns a FILETIME . This structure dates back to 32-bit systems, and consists of two 32-bit fields, dwHighDateTime and dwLowDateTime , which, combined together, define a 64-bit number of 100-nanosecond intervals since the epoch time.

In MSVC, we can call this function and trace its execution in a debugger. The disassembly in the 32-bit mode shows its code:

This is indeed very clever. The function contains no system calls. Everything happens in the user space. There is a memory area that is mapped into the address space of every process. Some background thread regularly updates three double-words there, which contain the high part of the value, the low part, and the high part again. The client reads all three, in this order, and, if the two high parts are equal, the low part is considered consistent with them (the strong memory access ordering of x86 guarantees this). If not, the procedure must be repeated. This is very unlikely event, that’s why the entire procedure is so fast.

The function looks even better in the 64-bit mode:

Here the background thread can write the value atomically, and the client can atomically read it. Actually, the code could have been even better – we could write a 64-bit value in one instruction:

However, the code is much faster than any possible system call anyway.

What is the resolution of this timer? Let’s collect the values into an array and print it later:

The timer ticks roughly once every half a millisecond (2000 Hz). This is perfectly adequate to serve as a base of currentTimeMillis() .

Linux version

This happened to be much longer journey than I expected. We start at another version of os::javaTimeMillis() , in hotspot/src/os/linux/vm/os_linux.cpp :

It is very unlikely that the time is spent in multiplication or division by 1000, but let’s measure the performance of gettimeofday anyway:

Running it, we get:

The call to gettimeofday is indeed slow. Why? Is there perhaps some system call involved? Let’s run it in the gdb . After we have stepped into the call of gettimeofday , we see this:

The first jmpq in fact jumps to the next instruction ( +6 ), and eventually we end up in a function called _dl_runtime_resolve .

What is actually happening here is that we are linking to the vDSO (virtual dynamic shared object), which is a small fully relocatable shared library pre-mapped into the user address space. The linking happens during the first call of gettimeofday , after which the call is resolved, and the first indirect jump goes straight into the function. Let’s skip this execution and break before the next call to gettimeofday . The function looks the same:

but this time the jump takes us to the real implementation. Here it is, with some added comments:

The vDSO is mapped to a new address each time (probably, some security feature). However, the function is always placed at the same address as above ( 0x00007ffff7ffae50 ) when run under gdb (I’m not sure what causes this). The function accesses some memory at addresses not far from its code (such as 0x7ffff7ff8080 or 0x7ffff7ff90f0 ), always using the “relative-to-IP” addressing mode. This makes this function completely relocatable – it can be mapped to any address in the user space, together with all its data.

We can see that the function employs various options. It can make a syscall , it can execute an rdtsc instruction, or it can just read some memory (in a similar fashion to the Windows implementation). Which of these options is applied in our case?

Let’s look at the source code of our version of Linux kernel (3.17.4). Note that this is not the latest one (Linux distributors are very conservative people). The code of the latest one differs quite a bit (including some constant definitions). Here is the code (in arch/x86/vdso/vclock_gettime.c ):

Here is the rest of the code ( gtod , obviously, stands for gettimeofday ). In arch/x86/include/asm/vgtod.h :

What is going on here is very close to what happened in Windows. There is a data structure that accompanies the vDSO and is mapped to the address space of every process. It is called vvar_vsyscall_gtod_data and is addressed in the code via the pseudo-variable gtod . In the assembly listing above this structure sits at the address 0x7ffff7ff8080 .

Some background thread updates the fields of this structure at regular intervals. Since there are more than two of these fields, the Windows trick of writing the high part of the number twice doesn’t work. However, a similar trick works. The writer maintains a version number of the data in the structure (the seq field). It gets incremented by one when the writer starts updating the structure, and again by one after it finishes (with appropriate write barrier instruction being used). As a result, the odd value means that the data isn’t consistent. The reader must read the number, make sure it’s even (wait a bit using pause instruction if not), read all the values of interest from the structure, read the version number again, and if it is the same as in the beginning, the data is considered correct. This is what gtod_read_begin and gtod_read_retry functions are for.

Read barrier instructions must be used to make sure that the processor didn’t re-order reading the version number with reading of the actual data. However, the strong memory ordering of Intel x86 makes this unnecessary, so the read barrier call ( smp_rnb() ) is empty in our case.

The values of interest are wall_time_sec and wall_time_snsec . The first one is the proper number of seconds since the epoch as reported by the good old time() call. In fact, time() is implemented by reading exactly this value, and without any locking or version control:

The second value is the nanoseconds time, relative to the last second, shifted left by gtod->shift , Or, rather, it the time measured in some units, which becomes the nanosecond time when shifted right by gtod->shift , which, in our case, is 25. This is very high precision. One nanosecond divided by 2 25 is about 30 attoseconds. Perhaps, Linux kernel designers looked far ahead, anticipating the future when we’ll need such accuracy for our time measurement.

What is the real resolution of these time values? Let’s read these values in a loop and print them when they change.

Unless we want to run the program in gdb , we must first resolve the vDSO relocation. That’s easy:

Now we can run a loop to detect change in wall_time_snsec :

A typical fragment of the output:

We see that the wall_time_nsec value is updated rather infrequently: for about 650000 iterations it stays the same and then jumps by a big value, which, being shifted by shift , becomes 999990 ns, or almost exactly one millisecond. It’s also interesting to see what happens when wall_time_sec changes:

The new nanosecond value doesn’t start at zero; it starts at 652692, which is over half a millisecond. It’s not always that big – I saw values of 200K and 300K. In short, two variables available in the gtod structure provide very fast access to a rather coarse time value with a one millisecond resolution. Just like in Windows.

However, gettimeofday() does not stop here. It tries to improve accuracy using other methods. In the code above this is what the vgetsns() function is responsible for:

The idea is that somewhere in the system there is a high-frequency timer, which we can ask for a current tick count. We record the reading of that timer at the last tick of the coarse timer ( gtod->cycle_last ), and get the difference. The gtod->mask and gtod->mult fields help convert the reading of that timer to our 30-attosecond units. This can explain the choice of those units: it’s not that Linux designers wanted to measure times of molecular processes; the unit is chosen very small to reduce the errors during this conversion.

The code above provides for three types of high-frequency timers: TSC (based on the RDTSC instruction of Intel x86 instruction set), HPET (based on the external hardware, the HPET chip), and PVCLOCK , which, most probably, has something to do with running in the VM environment.

In any case, the code only makes use of the high-precision timer to get the offset from the coarse time. The timer doesn’t have to run very stable for years. It is only supposed to be stable enough to provide the time offset between the coarse timer ticks. Moreover, its clock frequency may vary, as long as that variance is known to the system and reflected in appropriate update of the mask and mult fields. The system is very well designed to provide very accurate time very fast. So what went wrong?

In our case, the vclock_mode field is set to 2, which means using the HPET (High Precision Event Timer). This timer is a hardware piece included into all modern PCs: a replacement of the good old 8253/8254 chip. That chip ran a counter, which, upon reaching zero, could trigger an interrupt, and could also be read directly using IN instructions. It ran at the frequency of 1.19318 MHz, and, if programmed to expire every 65536 clocks, caused an interrupt every 55 ms (18.2 Hz), which we all remember since the MS DOS days.

The HPET runs at higher frequency, counts up rather than down, and is available via memory-mapped I/O rather than I/O ports. The routine to read the counter looks like this:

This is compiled into just one 32-bit memory read instruction:

The entire gettimeofday in the case of a HPET time source is compiled into a sequence of memory reads plus some shifts and multiplications. No divisions or other expensive operations are present. Why is it then executing so slowly?

I didn’t have a sampling profiler available on the test machine, but there is a poor man’s solution: to attach gdb to a running program that executes gettimeofday in a loop, and then interrupt it. In all the cases that I tried it it always stopped at exactly one place: after the above-mentioned instruction (at gettimeofdat+550 ). This indicates that that instructions alone takes most of the execution time. This is easy to verify. Let’s resolve the hpet_page in a similar way to resolving vsyscall_gtod_data and read this variable in a loop:

It takes more than half a microsecond to read the data from a HPET timer, which makes it a very slow time source. If we print the values we read from this timer, we’ll see that, unlike in the case of gtod->wall_time_snsec , the values change each time we access the timer. The typical difference is 8, which means that the timer ticks 8 times while being read. The timer’s accuracy is way higher than its performance. The difference between timer values measured at the points when the coarse nanoseconds change (which, as we learned, happens every millisecond), is 14320, which means that the HPET frequency is 14.32 MHz (70 ns per tick)..

This is really sad: the timer runs at high frequency, but there is no way to read it at this frequency. We can read it at about 1.8 MHz, which is just a bit higher than the frequency of the MS DOS timer.

And the worst part is that in our use case we are in fact uninterested in such a high accuracy anyway – all we need is currentTimeMillis() , and the coarse timer is perfectly suitable for that.

The coarse timer

There is an easy way out, because the coarse timer is available via the user API: the clock_gettime() function:

Among multiple values defined in <time.h> for clk_id there is one that does exactly what we need: CLOCK_REALTIME_COARSE (value 5). The code for this function is present in the same vDSO ( vclock_gettime.c ):

The nanosecond times reported by this function are 999991 ns (1 ms) apart, and the execution takes 8.4 ns.

The TSC time source

We’ve learned that the slow execution of currentTimeMillis() was caused by two factors:

  • JVM using gettimeofday() instead of clock_gettime()
  • gettimeofday() being very slow if HPET time source is used.

HPET, however, isn’t the only time source these days. Perhaps, not even the most common: many systems use TSC. This could make the entire study irrelevant. However, in our project the servers are configured with the HPET time source for a reason: this time source integrated well with the NTP client, allowing for smooth time adjustment, while TSC was not as stable (I don’t know the details; this is what the local Linux gurus say and I have no option but to trust them). Some other developers may find themselves in the same situation. Besides, a Java developer can’t always know what time source will be configured on the machine the program will run. That’s why I feel that the findings made so far are still important.

However, it is still interesting to find out how the result would change if we used the TSC time source. TSC stands for the time stamp counter, which is simply the number of CPU cycles counted since the startup time (it is only 64-bit wide, so it will wrap around in 243 years at 2.4GHz clock rate). This value can be read using rdtsc instruction. Traditionally, there were two problems with this value:

  • the values from different cores or physical processors may be shifted against each other, as the processors may start at different times
  • the clock frequency of a processor may change during execution.

The first one seems to be indeed a problem. I tried getting the rdtsc value from several cores at once, synchronised on writing into some memory location. Even in the best case I got differences of a couple of thousand cycles. Sometimes more. However, this is only a problem if the programmer wants to use the TSC manually; in this case the thread affinity must be set accordingly. The OS knows when it re-schedules threads from one core to another, so it can make all necessary adjustments.

The second problem seems to be a thing of the past. The Intel doc says this:

  • For Pentium M processors (family [06H], models [09H, 0DH]); for Pentium 4 processors, Intel Xeon processors (family [0FH], models [00H, 01H, or 02H]); and for P6 family processors: the time-stamp counter increments with every internal processor clock cycle. The internal processor clock cycle is determined by the current core-clock to busclock ratio. Intel® SpeedStep® technology transitions may also impact the processor clock.

  • For Pentium 4 processors, Intel Xeon processors (family [0FH], models [03H and higher]); for Intel Core Solo and Intel Core Duo processors (family [06H], model [0EH]); for the Intel Xeon processor 5100 series and Intel Core 2 Duo processors (family [06H], model [0FH]); for Intel Core 2 and Intel Xeon processors (family [06H], DisplayModel [17H]); for Intel Atom processors (family [06H], DisplayModel [1CH]): the time-stamp counter increments at a constant rate.

Processor’s support for invariant TSC is indicated by CPUID.80000007H:EDX[8].

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *