Interesting. I ran the test with

openjdk version "16" 2021-03-16
OpenJDK Runtime Environment (build 16+36-2231)
OpenJDK 64-Bit Server VM (build 16+36-2231, mixed mode, sharing)

and it passed. I then ran it with 

openjdk version "11.0.2" 2019-01-15
OpenJDK Runtime Environment 18.9 (build 11.0.2+9)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)

and

java version "11.0.9" 2020-10-20 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.9+7-LTS)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.9+7-LTS, mixed mode)

and

java version "11.0.10" 2021-01-19 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.10+8-LTS-162)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.10+8-LTS-162, mixed mode)

and it failed with all of them.

Ralph


> On Apr 6, 2021, at 3:22 PM, Ralph Goers <ralph.go...@dslextreme.com> wrote:
> 
> I am using 
> 
> openjdk version "11.0.10" 2021-01-19 LTS
> OpenJDK Runtime Environment Corretto-11.0.10.9.1 (build 11.0.10+9-LTS)
> OpenJDK 64-Bit Server VM Corretto-11.0.10.9.1 (build 11.0.10+9-LTS, mixed 
> mode)
> 
> Our test uses
> <dependency>
>  <groupId>com.google.code.java-allocation-instrumenter</groupId>
>  <artifactId>java-allocation-instrumenter</artifactId>
>  <version>3.3.0</version>
> </dependency>
> to validate that it is not allocating any objects.
> 
> If you want to test it yourself you can do
> 
> git clone https://github.com/apache/logging-log4j2.git 
> <https://github.com/apache/logging-log4j2.git>
> cd logging-log4j2
> mvn -DskipTests=true clean install
> cd log4j-core
> mvn surefire:test -Dtest=GcFreeSynchronousLoggingTest 
> -Dmaven.surefire.debug="-Dlog4j2.usePreciseClock=true”
> 
> When it fails you will see a strange message with 
> 
> I just allocated the object 2021-04-06T21:55:20.878377Z of type 
> java/time/Instant whose size is 24 ==> expected: <true> but was: <false>
> 
> Ralph
> 
>> On Apr 6, 2021, at 9:53 AM, Peter Levart <peter.lev...@gmail.com> wrote:
>> 
>> Hi Ralph,
>> 
>> 
>> Which version of JDK did you try running the code. I tried the following 
>> benchmark:
>> 
>> 
>> @BenchmarkMode(Mode.AverageTime)
>> @Fork(value = 1)
>> @Warmup(iterations = 5, time = 1)
>> @Measurement(iterations = 10, time = 1)
>> @OutputTimeUnit(TimeUnit.NANOSECONDS)
>> @State(Scope.Benchmark)
>> public class InstantBench {
>> 
>>    @Benchmark
>>    public long epochMilli() {
>>        Instant instant = Clock.systemUTC().instant();
>>        return instant.toEpochMilli();
>>    }
>> }
>> 
>> 
>> And didn't get any heap allocation on either JDK 11:
>> 
>> 
>> Benchmark Mode  Cnt   Score    Error   Units
>> InstantBench.epochMilli                      avgt   10  34.722 ±  0.328   
>> ns/op
>> InstantBench.epochMilli:·gc.alloc.rate       avgt   10  ≈ 10⁻⁴           
>> MB/sec
>> InstantBench.epochMilli:·gc.alloc.rate.norm  avgt   10  ≈ 10⁻⁵             
>> B/op
>> InstantBench.epochMilli:·gc.count            avgt   10     ≈ 0           
>> counts
>> 
>> 
>> ..nor on JDK 16:
>> 
>> 
>> Benchmark Mode  Cnt   Score    Error   Units
>> InstantBench.epochMilli                      avgt   10  33.612 ±  0.258   
>> ns/op
>> InstantBench.epochMilli:·gc.alloc.rate       avgt   10  ≈ 10⁻⁴           
>> MB/sec
>> InstantBench.epochMilli:·gc.alloc.rate.norm  avgt   10  ≈ 10⁻⁵             
>> B/op
>> InstantBench.epochMilli:·gc.count            avgt   10     ≈ 0           
>> counts
>> 
>> 
>> Regards, Peter
>> 
>> On 4/6/21 8:17 AM, Ralph Goers wrote:
>>> Yes, I am aware that the Instant is constructed from the two values. That 
>>> is exactly why I was hoping I could pass in an object where the values of 
>>> those two fields could be injected. This would still allow Instant to be 
>>> immutable but allow Log4j to update one of the pre-existing Clock instances 
>>> it is managing. That would require that a new Interface be defined with the 
>>> two set methods and a new static method in the Clock class.
>>> 
>>> Getting the value in microseconds would probably be a middle ground that 
>>> could also work considering that is the best resolution available on most 
>>> hardware today.
>>> 
>>> Whichever is implemented I suspect getting it back ported to Java 11 is 
>>> unlikely? According to the surveys I’ve seen the majority of users are 
>>> still on Java 8. Given that, I’d be surprised if there is a mad rush to 
>>> adopt Java 17 very soon. Given where things are I’m not even sure how 
>>> likely it is this could make it in for Java 17.  The irony here is that the 
>>> folks who need to run garbage free are also the ones most likely to need a 
>>> Clock with higher resolution. Right now they can’t have both.
>>> 
>>> Ralph
>>> 
>>>> On Apr 5, 2021, at 1:26 PM, Roger Riggs <roger.ri...@oracle.com> wrote:
>>>> 
>>>> Hi,
>>>> 
>>>> Java does not have a data type with enough resolution to hold a full 
>>>> nanosecond value.
>>>> Hence the implementation of Instant holding seconds and nanos.
>>>> 
>>>> There is an long dormant enhancement request to return micro-seconds as a 
>>>> long.
>>>> 8196003 <https://bugs.openjdk.java.net/browse/JDK-8196003> java.time 
>>>> Instant and Duration methods for microseconds
>>>> 
>>>> That might be useful if the application gets enough resolution from 
>>>> microseconds.
>>>> There might be some clever interpolation between System.currentTimeMillis()
>>>> and adjusting with System.nanoTime().
>>>> Though it would likely not be exactly synchronized with the values from 
>>>> Instant.
>>>> 
>>>> Regards, Roger
>>>> 
>>>> 
>>>> On 4/5/21 3:56 PM, Brian Goetz wrote:
>>>>> Project Valhalla will allow Instant to be migrated to a primitive class, 
>>>>> which would address your problem.
>>>>> 
>>>>> On 4/2/2021 7:47 PM, Ralph Goers wrote:
>>>>>> Log4j 2 supports the notion of a PreciseClock - one that can be 
>>>>>> initialized to something more precise than a millisecond. At the same 
>>>>>> time it also supports running with no heap allocations in certain 
>>>>>> circumstances. I am in the process of moving our master branch to 
>>>>>> require Java 11 as the minimum. In doing so I am encountering unit test 
>>>>>> errors while verifying that logging is garbage free. They all occur 
>>>>>> allocating an Instant.
>>>>>> 
>>>>>> The code we have simply does
>>>>>> 
>>>>>> public void init(MutableInstant mutableInstant) {
>>>>>>     Instant instant = java.time.Clock.systemUTC().instant();
>>>>>> mutableInstant.initFromEpochSecond(instant.getEpochSecond(), 
>>>>>> instant.getNano());
>>>>>> }
>>>>>> In our previous tests we had thought the allocation was being eliminated 
>>>>>> due to escape analysis since the data is being extracted from the 
>>>>>> Instant and not passed along. However, after upgrading the Google test 
>>>>>> library and the JDK version it appears that is not the case.
>>>>>> Ideally we would really like something like
>>>>>> 
>>>>>> public void init(MutableInstant mutableInstant) {
>>>>>>        java.time.Clock.systemUTC().initInstant(mutableInstant);
>>>>>> }
>>>>>> 
>>>>>> where Mutable instant would implement an interface that has the two set 
>>>>>> methods.The method would execute the same logic that is in the instant() 
>>>>>> method but instead of creating a new Instant it would call the set 
>>>>>> methods for the provided object.
>>>>>> 
>>>>>> This would allow us to either have the MutableInstants in ThreadLocals 
>>>>>> or some other mechanism to ensure they are thread safe and have no heap 
>>>>>> allocations. As it stands now I see no way to gain access to the higher 
>>>>>> precision clock without memory allocation.
>>>>>> 
>>>>>> Do you know of another way to do this? Am I missing something?
>>>>>> 
>>>>>> Ralph
>>>> 
>>> 
>> 
> 
> 


Reply via email to