RFR(S): 8067471: Use private static final char[0] for empty Strings

Claes Redestad claes.redestad at oracle.com
Fri Dec 19 14:25:39 UTC 2014


Hi again,

I just had a nagging thought. For applications reporting some or a lot 
of different empty String objects,
this might be due not to use of this constructor, but due to things like 
an empty StringBuilder.toString() ending
up calling java.lang.String(char[] value, int offset, int count) with a 
count of 0.

My feeling is that this would be a more probable/reasonable source of 
distinct empty strings in an application,
and would not be helped much by the current patch.

For completeness the java.lang.String(char/int[], int, int) constructors 
could have a check for count == 0 in
which case it assigns value to "".value, see 
http://cr.openjdk.java.net/~redestad/8067471/webrev.01/

Running a few simple microbenchmarks on this (sorry, Aleksey!)[1] yields 
a bit of speedup, at no discernible
cost for a quick sanity check of an actually copying path:

baseline:
Benchmark                            Mode  Samples    Score Error   Units
o.s.SimpleBench.emptySBBench        thrpt        5   48.213 ± 3.509  ops/us
o.s.SimpleBench.emptyString         thrpt        5  103.900 ± 4.869  ops/us
o.s.SimpleBench.emptyStringCount    thrpt        5  105.802 ± 3.355  ops/us
o.s.SimpleBench.emptyStringCountCP  thrpt        5  104.464 ± 8.251  ops/us
o.s.SimpleBench.singleStringCount   thrpt        5   76.379 ± 4.139  ops/us
o.s.SimpleBench.singleStringCountCP thrpt        5   88.202 ± 4.945  ops/us

experiment:
Benchmark                            Mode  Samples    Score Error   Units
o.s.SimpleBench.emptySBBench        thrpt        5  153.822 ± 7.900  
ops/us 3x
o.s.SimpleBench.emptyString         thrpt        5  183.588 ± 4.429  
ops/us 1.75x
o.s.SimpleBench.emptyStringCount    thrpt        5  166.457 ± 9.202  
ops/us 1.6x
o.s.SimpleBench.emptyStringCountCP  thrpt        5  168.089 ± 4.676  
ops/us 1.6x
o.s.SimpleBench.singleStringCount   thrpt        5   75.780 ± 8.490  
ops/us 1x
o.s.SimpleBench.singleStringCountCP thrpt        5   89.202 ± 3.714  
ops/us 1x

I guess we'd need to check that compiled code don't get messed up for a 
mix of inputs where count can be both
zero and non-zero with some probability.

/Claes

[1] public char[] emptyCharArray = new char[0];
     public int[] emptyIntArray = new int[0];

     public char[] singleCharArray = new char[] { 'x' };
     public int[] singleIntArray = new int[] { 102 };
     public StringBuilder emptySB = new StringBuilder();

     @Benchmark
     public String emptySBBench() {
         return emptySB.toString();
     }

     @Benchmark
     public String emptyString() {
         return new String();
     }

     @Benchmark
     public String emptyStringCount() {
         return new String(emptyCharArray, 0, 0);
     }

     @Benchmark
     public String emptyStringCountCP() {
         return new String(emptyIntArray, 0, 0);
     }

     @Benchmark
     public String singleStringCount() {
         return new String(singleCharArray, 0, 1);
     }

     @Benchmark
     public String singleStringCountCP() {
         return new String(singleIntArray, 0, 1);
     }


On 2014-12-19 01:41, Lev Priima wrote:
> Claes,
> Thanks for cool suggestion: 
> http://cr.openjdk.java.net/~lpriima/8067471/webrev.01/:
>   public java.lang.String();
>     Signature: ()V
>     flags: ACC_PUBLIC
>     LineNumberTable:
>       line 137: 0
>       line 138: 4
>       line 139: 13
>     Code:
>       stack=2, locals=1, args_size=1
>          0: aload_0
>          1: invokespecial #1                  // Method 
> java/lang/Object."<init>":()V
>          4: aload_0
>          5: ldc           #2                  // String
>          7: getfield      #3                  // Field value:[C
>         10: putfield      #3                  // Field value:[C
>         13: return
>       LineNumberTable:
>         line 137: 0
>         line 138: 4
>
>
> Aleksey ,
> By naive glance, new constructor thrpt is  bigger(jdk9/dev vs 9b42 in 
> attach) on:
>     @Benchmark
>     public String getNewString() {
>         return new String();
>     }
> Please suggest things to compare . Do we have bigapps-like benchmarks 
> which may show regression on this?
>
> On 17.12.2014 21:10, Aleksey Shipilev wrote:
>> On 17.12.2014 18:58, Claes Redestad wrote:
>>> On 2014-12-17 11:22, Lev Priima wrote:
>>>> Please review space optimization in no args String constructor.
>>>> Originally, it was already rejected once by suggestion in
>>>> http://mail.openjdk.java.net/pipermail/core-libs-dev/2012-May/010300.html 
>>>>
>>>> w/o formal justification of pros and contras.  And enhancement was
>>>> requested again by Nathan Reynolds.
>>>>
>>>> Issue: https://bugs.openjdk.java.net/browse/JDK-8067471
>>>> Patch: http://cr.openjdk.java.net/~lpriima/8067471/webrev.00/
>> I am OK with this change pending we understand the performance impact a
>> little better. Please do benchmarks.
>>
>>
>>> Could this have some obscure security implications, such as the ability
>>> to lock up some subsystem via retrieving and synchronizing on the 
>>> shared
>>> new String().value? I guess not, for practical purposes.
>> This would be an issue if we published String.value directly, but we 
>> don't.
>>
>>> I guess it could also be written:
>>>
>>>      public String() {
>>>          this.value = "".value;
>>>      }
>>>
>>> ... which would saves some byte code and should avoid having to 
>>> allocate
>>> a distinct object for this.
>> Oh, I like this trick, given "" is probably already in constant pool for
>> some other reason. However, there is an additional dereference. We need
>> a targeted nanobenchmark to properly quantify the costs for either 
>> variant.
>>
>> -Aleksey.
>>
>>
>




More information about the core-libs-dev mailing list