Skip to content

get rid of String.intern #332

Closed
Closed

Description

@vladimirdolzhenko

InternCache is used in jackson for json property names only and solves the problem of extra memory footprint.
In the same time it uses String.intern - that is not an issue rather than misusage of it. The purpose of using string.intern is jvm specific: usually it maintains string literal pool (and covers internal jvm cases).

There are several known drawbacks of using intern:

  • String pool Hashtable is non-resizable table. That means: we suffer of hash code collisions when table will have items more than its size (suffer as much as many items in string.intern pool)
  • The other drawback is involve GC into that: string intern pool is subject for minor collection too. So, app suffers of using string.intern implicitly via GC phases.

There is no use cases of check the equality of strings using == and therefore no any reasons of using String.intern - the biggest profit is to have string deduplication (already achieved).

patch

===================================================================
--- src/main/java/com/fasterxml/jackson/core/util/InternCache.java  (revision 489becbfb28a41980f0d5147d6069b30fa3b5864)
+++ src/main/java/com/fasterxml/jackson/core/util/InternCache.java  (revision )
@@ -58,7 +58,7 @@
                 }
             }
         }
-        result = input.intern();
+        result = input;
         put(result, result);
         return result;
     }

Test case

The general idea behind syntenic json is used in test (a flat json structure with 10000 properties with prefix someQName) is provide lots of different property names to trigger usage of string.intern within InternCache - that is close to real apps use cases where amount of unique property names is on a scale from hundreds to thousands.

{
    "someQName0": 0,
    "someQName1": 1,
    ....
    "someQName9999": 9999
}

JMH test

Let's measure performance of a single json parse with interting and w/o it: PerfInternCache.java

Benchmark                   Mode  Cnt     Score     Error  Units
-PerfInternCache.intern      avgt    5  4098.696 ± 164.484  us/op
+PerfInternCache.noIntern    avgt    5  2320.159 ± 204.301  us/op

GC

Another test is to measure how intern is affecting implicitly via GC: handle 10k the same jsons as we use in previous test (the use case is very very close to real one in real apps like web service / microsevices):
InternCache GC timings java test

Run it with -verbose:gc -XX:+PrintGCDetails -Xloggc:gc.log

and after that get the total GC pause time
$ grep "GC" gc.log | grep "Times: " | awk '{S+=$8}END{print S}'

-intern      0.1907254 +- 0.00469 sec
+w/o intern  0.07665   +- 0.00498 sec

Conclusion

Using intern harms application performance as explicitly via more expencive InternCache.intern and implicitly via GC. In the same time we keep memory footprint on the same low possible level.

Activity

  1. added a commit that references this issue on Nov 13, 2016
    3723da9
  2. IRus commented on Nov 13, 2016

    @IRus

    @vladimirdolzhenko what about other property sizes? Like 10, 100, 1000, 100_000?

  3. added a commit that references this issue on Nov 13, 2016
    10af380
  4. vladimirdolzhenko commented on Nov 13, 2016

    @vladimirdolzhenko
    Author

    @IRus

    GC pauses in ms

    name/size 100 1000 10k 100k
    intern 2.249 ± 0.229 14.288 ± 0.554 192.375 ± 5.801 1395.513 ± 33.007
    w/o intern 1.987 ± 0.130 11.974 ± 0.279 75.822 ± 6.693 828.542 ± 74.948
  5. IRus commented on Nov 13, 2016

    @IRus

    What about PerfInternCache?

  6. vladimirdolzhenko commented on Nov 13, 2016

    @vladimirdolzhenko
    Author

    @IRus

    PerfInternCache in us/op:

    name 100 1000 10k 100k
    intern 13.879 ± 0.523 377.937 ± 27.612 4098.696 ± 164.484 31396.578 ± 2325.685
    w/o intern 13.472 ± 0.438 205.322 ± 9.720 2320.159 ± 204.301 17292.832 ± 1290.865
  7. cowtowncoder commented on Nov 14, 2016

    @cowtowncoder
    Member

    There is JsonFactory.Feature.INTERN_FIELD_NAMES to use if you prefer no intern()ing.]

  8. vladimirdolzhenko commented on Nov 14, 2016

    @vladimirdolzhenko
    Author

    @cowtowncoder I know about that flag - the problem that it is enabled by default and that kind of optimisation harms app more neither having it switched off.

    What's the point to have such kind of degradation flag rather to fix it and make apps (bearing in mind the spread around of json) using jackon feel a bit better ?

  9. vladimirdolzhenko commented on Nov 14, 2016

    @vladimirdolzhenko
    Author

    @cowtowncoder

    disabled flag INTERN_FIELD_NAMES PerfInternCache2.java (the numbers from another box - no any reasons to compare absolute values with prev one)

    Benchmark                  (intern)  (size)  Mode  Cnt      Score      Error  Units
    PerfInternCache.parseJson      true     100  avgt    5      5.968 ±    0.066  us/op
    PerfInternCache.parseJson      true    1000  avgt    5     74.035 ±   10.903  us/op
    PerfInternCache.parseJson      true   10000  avgt    5    878.049 ±   13.579  us/op
    PerfInternCache.parseJson      true  100000  avgt    5  26794.594 ± 8337.486  us/op
    PerfInternCache.parseJson     false     100  avgt    5      5.814 ±    0.635  us/op
    PerfInternCache.parseJson     false    1000  avgt    5     72.724 ±    1.257  us/op
    PerfInternCache.parseJson     false   10000  avgt    5    937.059 ±  297.084  us/op
    PerfInternCache.parseJson     false  100000  avgt    5  10000.686 ±  333.828  us/op
    

    I would really consider to disable that flag by default.

  10. vlsi commented on Nov 14, 2016

    @vlsi

    @vladimirdolzhenko , would you please add -prof gc (aka .addProfiler(GCProfiler.class)) outputs? (I mainly mean gc.alloc.rate.norm)

  11. vladimirdolzhenko commented on Nov 14, 2016

    @vladimirdolzhenko
    Author

    @vlsi that's all for Enabled/Disable flag only

    Benchmark                                                   (intern)  (size)  Mode  Cnt         Score          Error   Units
    PerfInternCache.parseJson                                       true     100  avgt    5         5.817 ±        0.084   us/op
    PerfInternCache.parseJson:·gc.alloc.rate                        true     100  avgt    5        70.810 ±        1.022  MB/sec
    PerfInternCache.parseJson:·gc.alloc.rate.norm                   true     100  avgt    5       432.005 ±        0.001    B/op
    PerfInternCache.parseJson:·gc.churn.PS_Eden_Space               true     100  avgt    5        71.433 ±      106.854  MB/sec
    PerfInternCache.parseJson:·gc.churn.PS_Eden_Space.norm          true     100  avgt    5       436.155 ±      658.138    B/op
    PerfInternCache.parseJson:·gc.churn.PS_Survivor_Space           true     100  avgt    5         0.436 ±        3.689  MB/sec
    PerfInternCache.parseJson:·gc.churn.PS_Survivor_Space.norm      true     100  avgt    5         2.672 ±       22.601    B/op
    PerfInternCache.parseJson:·gc.count                             true     100  avgt    5         6.000                 counts
    PerfInternCache.parseJson:·gc.time                              true     100  avgt    5         5.000                     ms
    PerfInternCache.parseJson                                       true    1000  avgt    5        74.988 ±        1.181   us/op
    PerfInternCache.parseJson:·gc.alloc.rate                        true    1000  avgt    5         5.494 ±        0.087  MB/sec
    PerfInternCache.parseJson:·gc.alloc.rate.norm                   true    1000  avgt    5       432.070 ±        0.021    B/op
    PerfInternCache.parseJson:·gc.churn.PS_Eden_Space               true    1000  avgt    5        12.783 ±      110.068  MB/sec
    PerfInternCache.parseJson:·gc.churn.PS_Eden_Space.norm          true    1000  avgt    5      1006.205 ±     8663.731    B/op
    PerfInternCache.parseJson:·gc.count                             true    1000  avgt    5         1.000                 counts
    PerfInternCache.parseJson:·gc.time                              true    1000  avgt    5         2.000                     ms
    PerfInternCache.parseJson                                       true   10000  avgt    5       909.903 ±        9.144   us/op
    PerfInternCache.parseJson:·gc.alloc.rate                        true   10000  avgt    5         0.495 ±        0.005  MB/sec
    PerfInternCache.parseJson:·gc.alloc.rate.norm                   true   10000  avgt    5       472.822 ±        0.008    B/op
    PerfInternCache.parseJson:·gc.count                             true   10000  avgt    5           ≈ 0                 counts
    PerfInternCache.parseJson                                       true  100000  avgt    5     25160.112 ±     2143.684   us/op
    PerfInternCache.parseJson:·gc.alloc.rate                        true  100000  avgt    5       388.930 ±       33.593  MB/sec
    PerfInternCache.parseJson:·gc.alloc.rate.norm                   true  100000  avgt    5  10258886.390 ±        2.419    B/op
    PerfInternCache.parseJson:·gc.churn.PS_Eden_Space               true  100000  avgt    5       361.152 ±      781.211  MB/sec
    PerfInternCache.parseJson:·gc.churn.PS_Eden_Space.norm          true  100000  avgt    5   9605141.415 ± 20734528.664    B/op
    PerfInternCache.parseJson:·gc.churn.PS_Survivor_Space           true  100000  avgt    5         0.555 ±        2.995  MB/sec
    PerfInternCache.parseJson:·gc.churn.PS_Survivor_Space.norm      true  100000  avgt    5     14668.970 ±    78754.601    B/op
    PerfInternCache.parseJson:·gc.count                             true  100000  avgt    5         4.000                 counts
    PerfInternCache.parseJson:·gc.time                              true  100000  avgt    5         9.000                     ms
    PerfInternCache.parseJson                                      false     100  avgt    5         6.237 ±        0.249   us/op
    PerfInternCache.parseJson:·gc.alloc.rate                       false     100  avgt    5        66.044 ±        2.671  MB/sec
    PerfInternCache.parseJson:·gc.alloc.rate.norm                  false     100  avgt    5       432.006 ±        0.001    B/op
    PerfInternCache.parseJson:·gc.churn.PS_Eden_Space              false     100  avgt    5        71.421 ±       98.050  MB/sec
    PerfInternCache.parseJson:·gc.churn.PS_Eden_Space.norm         false     100  avgt    5       468.240 ±      662.365    B/op
    PerfInternCache.parseJson:·gc.churn.PS_Survivor_Space          false     100  avgt    5         0.439 ±        3.517  MB/sec
    PerfInternCache.parseJson:·gc.churn.PS_Survivor_Space.norm     false     100  avgt    5         2.844 ±       22.728    B/op
    PerfInternCache.parseJson:·gc.count                            false     100  avgt    5         6.000                 counts
    PerfInternCache.parseJson:·gc.time                             false     100  avgt    5        13.000                     ms
    PerfInternCache.parseJson                                      false    1000  avgt    5        81.275 ±       27.249   us/op
    PerfInternCache.parseJson:·gc.alloc.rate                       false    1000  avgt    5         5.099 ±        1.653  MB/sec
    PerfInternCache.parseJson:·gc.alloc.rate.norm                  false    1000  avgt    5       432.076 ±        0.040    B/op
    PerfInternCache.parseJson:·gc.churn.PS_Eden_Space              false    1000  avgt    5        12.792 ±      110.141  MB/sec
    PerfInternCache.parseJson:·gc.churn.PS_Eden_Space.norm         false    1000  avgt    5      1221.939 ±    10521.259    B/op
    PerfInternCache.parseJson:·gc.count                            false    1000  avgt    5         1.000                 counts
    PerfInternCache.parseJson:·gc.time                             false    1000  avgt    5         3.000                     ms
    PerfInternCache.parseJson                                      false   10000  avgt    5       782.931 ±       23.195   us/op
    PerfInternCache.parseJson:·gc.alloc.rate                       false   10000  avgt    5         0.576 ±        0.017  MB/sec
    PerfInternCache.parseJson:·gc.alloc.rate.norm                  false   10000  avgt    5       472.707 ±        0.020    B/op
    PerfInternCache.parseJson:·gc.count                            false   10000  avgt    5           ≈ 0                 counts
    PerfInternCache.parseJson                                      false  100000  avgt    5     10713.712 ±      554.663   us/op
    PerfInternCache.parseJson:·gc.alloc.rate                       false  100000  avgt    5       774.272 ±       39.791  MB/sec
    PerfInternCache.parseJson:·gc.alloc.rate.norm                  false  100000  avgt    5   8698649.639 ±        0.518    B/op
    PerfInternCache.parseJson:·gc.churn.PS_Eden_Space              false  100000  avgt    5       815.419 ±      569.952  MB/sec
    PerfInternCache.parseJson:·gc.churn.PS_Eden_Space.norm         false  100000  avgt    5   9175274.322 ±  6805920.526    B/op
    PerfInternCache.parseJson:·gc.churn.PS_Survivor_Space          false  100000  avgt    5         2.493 ±        3.869  MB/sec
    PerfInternCache.parseJson:·gc.churn.PS_Survivor_Space.norm     false  100000  avgt    5     27976.282 ±    43304.868    B/op
    PerfInternCache.parseJson:·gc.count                            false  100000  avgt    5        12.000                 counts
    PerfInternCache.parseJson:·gc.time                             false  100000  avgt    5        38.000                     ms
    
  12. vlsi commented on Nov 14, 2016

    @vlsi

    That does look strange.
    intern=true => allocates 10258886 bytes/iteration ~ 9.8 MiB/iteration
    intern=false => allocates 8698649/iteration ~ 8.3 MiB/iteration

    It sounds like "string intern makes memory allocation higher", that is counter-intuitive, at least at the first sight

  13. IRus commented on Nov 14, 2016

    @IRus

    This benchmark was very short in terms of time, there are results by @isopov:

    https://gist.github.com/isopov/b215f473deb4ef40118c00f054dc204c

    Benchmark                                                   (intern)  (size)  Mode  Cnt         Score        Error   Units
    PerfInternCache.parseJson                                       true     100  avgt   25         5.582 ±      0.092   us/op
    PerfInternCache.parseJson:·gc.alloc.rate                        true     100  avgt   25        76.578 ±      1.262  MB/sec
    PerfInternCache.parseJson:·gc.alloc.rate.norm                   true     100  avgt   25       448.001 ±      0.001    B/op
    PerfInternCache.parseJson:·gc.churn.PS_Eden_Space               true     100  avgt   25        76.267 ±      4.881  MB/sec
    PerfInternCache.parseJson:·gc.churn.PS_Eden_Space.norm          true     100  avgt   25       446.030 ±     26.011    B/op
    PerfInternCache.parseJson:·gc.churn.PS_Survivor_Space           true     100  avgt   25         0.030 ±      0.007  MB/sec
    PerfInternCache.parseJson:·gc.churn.PS_Survivor_Space.norm      true     100  avgt   25         0.178 ±      0.041    B/op
    PerfInternCache.parseJson:·gc.count                             true     100  avgt   25       156.000               counts
    PerfInternCache.parseJson:·gc.time                              true     100  avgt   25       163.000                   ms
    PerfInternCache.parseJson                                       true    1000  avgt   25        64.844 ±      3.353   us/op
    PerfInternCache.parseJson:·gc.alloc.rate                        true    1000  avgt   25         6.616 ±      0.306  MB/sec
    PerfInternCache.parseJson:·gc.alloc.rate.norm                   true    1000  avgt   25       448.012 ±      0.001    B/op
    PerfInternCache.parseJson:·gc.churn.PS_Eden_Space               true    1000  avgt   25         6.695 ±      6.825  MB/sec
    PerfInternCache.parseJson:·gc.churn.PS_Eden_Space.norm          true    1000  avgt   25       456.683 ±    468.284    B/op
    PerfInternCache.parseJson:·gc.churn.PS_Survivor_Space           true    1000  avgt   25         0.040 ±      0.060  MB/sec
    PerfInternCache.parseJson:·gc.churn.PS_Survivor_Space.norm      true    1000  avgt   25         2.808 ±      4.220    B/op
    PerfInternCache.parseJson:·gc.count                             true    1000  avgt   25         9.000               counts
    PerfInternCache.parseJson:·gc.time                              true    1000  avgt   25        25.000                   ms
    PerfInternCache.parseJson                                       true   10000  avgt   25       727.737 ±     24.616   us/op
    PerfInternCache.parseJson:·gc.alloc.rate                        true   10000  avgt   25         0.641 ±      0.020  MB/sec
    PerfInternCache.parseJson:·gc.alloc.rate.norm                   true   10000  avgt   25       488.132 ±      0.004    B/op
    PerfInternCache.parseJson:·gc.count                             true   10000  avgt   25           ≈ 0               counts
    PerfInternCache.parseJson                                       true  100000  avgt   25     23717.280 ±   1526.254   us/op
    PerfInternCache.parseJson:·gc.alloc.rate                        true  100000  avgt   25       416.108 ±     24.800  MB/sec
    PerfInternCache.parseJson:·gc.alloc.rate.norm                   true  100000  avgt   25  10281132.282 ±   9036.086    B/op
    PerfInternCache.parseJson:·gc.churn.PS_Eden_Space               true  100000  avgt   25       428.100 ±     37.795  MB/sec
    PerfInternCache.parseJson:·gc.churn.PS_Eden_Space.norm          true  100000  avgt   25  10577485.930 ± 661648.403    B/op
    PerfInternCache.parseJson:·gc.churn.PS_Survivor_Space           true  100000  avgt   25         0.254 ±      0.160  MB/sec
    PerfInternCache.parseJson:·gc.churn.PS_Survivor_Space.norm      true  100000  avgt   25      6442.022 ±   4240.444    B/op
    PerfInternCache.parseJson:·gc.count                             true  100000  avgt   25       148.000               counts
    PerfInternCache.parseJson:·gc.time                              true  100000  avgt   25       390.000                   ms
    PerfInternCache.parseJson                                      false     100  avgt   25         5.721 ±      0.277   us/op
    PerfInternCache.parseJson:·gc.alloc.rate                       false     100  avgt   25        74.963 ±      3.536  MB/sec
    PerfInternCache.parseJson:·gc.alloc.rate.norm                  false     100  avgt   25       448.001 ±      0.001    B/op
    PerfInternCache.parseJson:·gc.churn.PS_Eden_Space              false     100  avgt   25        75.394 ±      5.015  MB/sec
    PerfInternCache.parseJson:·gc.churn.PS_Eden_Space.norm         false     100  avgt   25       450.417 ±     19.277    B/op
    PerfInternCache.parseJson:·gc.churn.PS_Survivor_Space          false     100  avgt   25         0.029 ±      0.009  MB/sec
    PerfInternCache.parseJson:·gc.churn.PS_Survivor_Space.norm     false     100  avgt   25         0.173 ±      0.054    B/op
    PerfInternCache.parseJson:·gc.count                            false     100  avgt   25       155.000               counts
    PerfInternCache.parseJson:·gc.time                             false     100  avgt   25       124.000                   ms
    PerfInternCache.parseJson                                      false    1000  avgt   25        64.212 ±      1.366   us/op
    PerfInternCache.parseJson:·gc.alloc.rate                       false    1000  avgt   25         6.659 ±      0.139  MB/sec
    PerfInternCache.parseJson:·gc.alloc.rate.norm                  false    1000  avgt   25       448.012 ±      0.001    B/op
    PerfInternCache.parseJson:·gc.churn.PS_Eden_Space              false    1000  avgt   25         7.439 ±      6.966  MB/sec
    PerfInternCache.parseJson:·gc.churn.PS_Eden_Space.norm         false    1000  avgt   25       497.387 ±    465.968    B/op
    PerfInternCache.parseJson:·gc.churn.PS_Survivor_Space          false    1000  avgt   25         0.039 ±      0.055  MB/sec
    PerfInternCache.parseJson:·gc.churn.PS_Survivor_Space.norm     false    1000  avgt   25         2.611 ±      3.689    B/op
    PerfInternCache.parseJson:·gc.count                            false    1000  avgt   25        10.000               counts
    PerfInternCache.parseJson:·gc.time                             false    1000  avgt   25        23.000                   ms
    PerfInternCache.parseJson                                      false   10000  avgt   25       736.818 ±     21.847   us/op
    PerfInternCache.parseJson:·gc.alloc.rate                       false   10000  avgt   25         0.633 ±      0.019  MB/sec
    PerfInternCache.parseJson:·gc.alloc.rate.norm                  false   10000  avgt   25       488.133 ±      0.004    B/op
    PerfInternCache.parseJson:·gc.count                            false   10000  avgt   25           ≈ 0               counts
    PerfInternCache.parseJson                                      false  100000  avgt   25      9299.408 ±    362.041   us/op
    PerfInternCache.parseJson:·gc.alloc.rate                       false  100000  avgt   25       894.241 ±     34.802  MB/sec
    PerfInternCache.parseJson:·gc.alloc.rate.norm                  false  100000  avgt   25   8697698.945 ±   7290.094    B/op
    PerfInternCache.parseJson:·gc.churn.PS_Eden_Space              false  100000  avgt   25       913.435 ±     50.882  MB/sec
    PerfInternCache.parseJson:·gc.churn.PS_Eden_Space.norm         false  100000  avgt   25   8890667.834 ± 449734.489    B/op
    PerfInternCache.parseJson:·gc.churn.PS_Survivor_Space          false  100000  avgt   25         1.296 ±      0.654  MB/sec
    PerfInternCache.parseJson:·gc.churn.PS_Survivor_Space.norm     false  100000  avgt   25     12487.399 ±   6192.507    B/op
    PerfInternCache.parseJson:·gc.count                            false  100000  avgt   25       227.000               counts
    PerfInternCache.parseJson:·gc.time                             false  100000  avgt   25       422.000                   ms
    
  14. cowtowncoder commented on Nov 14, 2016

    @cowtowncoder
    Member

    @vladimirdolzhenko I don't agree with our assessment of its harmfulness, based on invalid (as general statement) assumptions on usage. Performance to test is for databinding; and databind does in fact benefit from equality checks. You would have known this if you had asked; or perhaps dug around.
    As to what is typical number of unique names, I don't know; but nor is there anything to suggest specific number.

    I think it is very arrogant of you to make a demand of removing a feature that has served users well for, what, 7 years now. If you do not want the feature, turn it off. I keep mine on.

  15. 15 remaining items

  16. cowtowncoder commented on Dec 14, 2016

    @cowtowncoder
    Member

    @amanhigh This is very likely due to sub-optimal usage of ObjectMapper, and not due to interning. This method should not become hot spot unless domain of property names is unbounded.
    So while you can disable interning (and even canonicalization), this has so far always been due to either not reusing ObjectMapper, or, in some cases, not closing JsonParser instances (not reading until end-of-input OR closing).

    But if you have synthetic names, on the other hand, you do want to disable both (disabling canonicalization is actually sufficient).

  17. amanhigh commented on Jan 6, 2017

    @amanhigh

    @cowtowncoder We used single instance of Object Mapper with readValue operating on byte stream. We did get a significant improment in 99th Percentile after disabling intern caching.

    We haved Java Models for all classes and don't deserialize as map hence property names are bounded. I am not sure if same applies to Keys of hashMap as well as some properties are HashMaps with varying keys.

  18. cowtowncoder commented on Jan 6, 2017

    @cowtowncoder
    Member

    @amanhigh I do not doubt that you saw a significant improvement, but everything I know about interning and canonicalization suggests that the problem is because Symbol table is not being reused.
    If it was, interning would only ever occur once per unique String, and this really should not cause performance problems after steady state.

    Now... there are a few reasons why underlying, per-JsonFactory symbol table might not be reused.
    Not using same ObjectMapper (or, if low-level operation, JsonFactory) is a common cause but not the only one. Another common cause is that JsonParser instance is not closed, either implicitly (by reading all content and encountering end-of-input) or explicitly (via close()).

    But there is another possibility: if number of distinct symbols (property names) is really large (in order of tens of thousands), there is size limit (something like 40,000 or so), symbol table is not reused.
    This is a protective measure to prevent OOME and certain types of DoS attacks.

    I suppose that in latter case you could get into trouble because intern() calls would effectively not be cached. If this is the case, I would be interested in figuring out better ways to handle it.

    My main concern here is still the fact that I believe there are many use cases where intern()ing is beneficial, and that change to better support other use cases would have detrimental effect on these.

    I am also open to being proven wrong on benefits: for example, it is possible that newest JVMs may have different kinds of trade-offs. I hope to find a way to validate my knowledge here.
    The reason I may seem slow is simply that there are many important things to work on -- I think this is one too -- but juggling through many things makes it harder for me to get deep understanding.
    So even if it may not seem like it, I really appreciate your help @amanhigh (and everyone else too).

  19. cowtowncoder commented on Jan 6, 2017

    @cowtowncoder
    Member

    @amanhigh One more thing I just realized: your mention of Maps. This might be the problem wrt unbounded nature -- what matters at low level really is JSON keys.

    Would there be a way for you to locally modify jackson-core jar, adding minor diagnostics in ByteQuadsCanonicalizer? I know that's a lot to ask and wish there were hooks in place but those do not exist at this moment. But what would be interesting would be to know if table is flushed, and/or what happens when table is being closed.
    I suspect that you could well be hitting maximum size -- if so, symptoms would make sense.
    And this could guide efforts appropriately, addressing specific usage. And maybe make it easier to repro the issue with synthetic data?

  20. vladimirdolzhenko commented on Jan 6, 2017

    @vladimirdolzhenko
    Author

    @cowtowncoder I tried to persuade you that you don't understand String.intern properly and misuse it - with examples, benchmarks and so on - and I see that you would not take any of them into account

  21. relgames commented on Jan 10, 2017

    @relgames

    My main concern here is still the fact that I believe there are many use cases where intern()ing is beneficial, and that change to better support other use cases would have detrimental effect on these.

    Could you tell those use cases please? Maybe publish your benchmarks as well if you get some time.

  22. cowtowncoder commented on Jan 11, 2017

    @cowtowncoder
    Member

    @relgames testing I did was by modifying existing tests like:

    https://github.com/FasterXML/jackson-benchmarks
    https://github.com/cowtowncoder/java-json-performance-benchmarks

    by forcible disabling interning and comparing results to ones I get when interning is enabled.
    Similarly I have earlier tested jvm-serializers usage. All of these have relatively small vocabulary.

    While I would not mind sharing results (or someone else running those), I do not see enough value for me to spend more time on general question; and specifically not in being convinced to remove ability to intern() keys. Users are free to change the setting as they see fit; same goes for frameworks using Jackson.

  23. IRus commented on May 17, 2017

    @IRus
  24. relgames commented on May 20, 2017

    @relgames

    "In almost every project we were taking care of, removing String.intern() from the hotpaths, or optionally replacing it with a handrolled deduplicator, was the very profitable performance optimization. Do not use String.intern() without thinking very hard about it, okay?"

    Summary from JDK developer.

  25. cowtowncoder commented on May 20, 2017

    @cowtowncoder
    Member

    @relgames Yes? I have thought about it more than people commenting here. Article itself is fine and properly discusses challenges. It may indeed be the case that for some usage turning off intern()ing makes sense -- if you end up having hundreds of thousands of symbols. I do not think this applies to majority of JSON usage.

    Given that interning can easily be turned off by those who want to do that:

    factory.disable(JsonFactory.Feature.INTERN_FIELD_NAMES);
    

    and since there is clear performance improvement by test I have (jackson-benchmarks), there's little to discuss here. I do not see any point in removing the feature.
    At most it might make sense to figure if default settings should differ between usage via databinding (where intern()ing can help), or by direct setup as streaming parser (at which level there is likely no benefit) for Jackson 3.x. I will create a new issue for such discussion.

  26. locked and limited conversation to collaborators on May 20, 2017
  27. cowtowncoder commented on Nov 28, 2017

    @cowtowncoder
    Member

    For what it's worth, there's follow up issue: #378 -- and I have some good news to share there.

  28. added this to the 3.0.0 milestone on Aug 29, 2018
  29. cowtowncoder commented on Aug 29, 2018

    @cowtowncoder
    Member

    And for TL;DNR observers, Jackson 3.0 DOES NOT use String.intern() by default,
    and the implementation is such that it typically performs at same level with or without intern()ing, including usage through databinding.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions