News for hackers. Stuff that matters.
Re:JVM Anatomy Quark #10: String.intern (2019) by theandrewbailey on Wednesday June 22, 2022 @03:26PM (#31840684)
I've been writing Java for about 15 years. There hasn't been a single instance where I've used String.intern(). I'm starting to think that there is never an appropriate place for it. What do you get from it? Anything else other than the ability to reliably use == for String comparison instead of .equals()?

Nevermind the architecture astronauts saying that the string intern pool is actually global state and global state is always problematic.

Re:JVM Anatomy Quark #10: String.intern (2019) by bumper_crop on Wednesday June 22, 2022 @06:38PM (#31842765)
You'll need to go back earlier than 15 years. ConcurrentHashMap and friends were added in 1.6, but String.intern has been in there since the beginning. Since Java shipped with Threads in the standard library, (but no memory model) that meant it would have been very difficult to do concurrent String deduplication yourself. If you agree string deduplication is needed, then String.intern() was a good implementation for a long while.

String.intern() also offers some other benefits for certain use cases. Earlier versions of java did not cache the String hashcode, which meant that to use Strings as hash table keys meant hashing a lot more. But, an interned string can be used in an IdentityHashMap, which was faster for a long portion of Java's early life.

(I worked on a moderately popular Java library that targeted Java 1.5 as the minimum version. It does occasionally come up useful, but only in specific, and increasingly rare circumstances)

Re:JVM Anatomy Quark #10: String.intern (2019) by jamesfinlayson on Wednesday June 22, 2022 @08:53PM (#31843852)
That would explain it - I think I first touched Java in 2009 or 2010 and this is the first I've heard of String.intern in all that time.
Re:JVM Anatomy Quark #10: String.intern (2019) by hibikir on Thursday June 23, 2022 @12:43AM (#31845229)
I used it a few times for great effect, many years ago, when dealing with very data heavy Swing applications. We dealt with millions of rows of data, and some keys were strings with very low cardinality. Interning some of those columns lead to major reductions in memory usage: Tens of millions of copies of the same string aren't all that great for 2005 era laptop, as headroom between garbage collections, especially back in the day, could make a big difference. Now, the fact that the strings were comparable with == vs .equals? never bothered trying to play those games.

Now, could we have solved this with other ways other than interns? Absolutely: Just make deserialization code do mostly the very same thing, but done by hand and on the heap.

In today's JVM, if you are using the G1 garbage collector, you can get 99% of the memory savings almost completely for free with a VM parameter: You still pay for an extra object, just pointed to the same character array underneath. I'd argue it's one of the most underutilized flags out there: It's not uncommon to end up with half the memory footprint in many use cases, and memory savings are often runtime savings too. Now that we have this, maybe intern always loses, but it was definitely useful in the right circumstances.

Re:JVM Anatomy Quark #10: String.intern (2019) by Someone on Wednesday June 22, 2022 @04:11PM (#31841264)
I never understood it, either. It would be useful (at least historically) in cases where you’re willing to give up performance in exchange for lower memory usage, and then, you’d still have to make good choices on what strings to intern and what strings not to intern (I think the standard example is a XML parser that may choose to intern all node and attribute names of a XML schema. If you’re parsing a large file using a DOM parser, that can significantly decrease memory usage)

Problem 1: it can easily depend on your input data whether interning is necessary to prevent out of memory exceptions.

Problem 2: there often are better solutions to memory usage than interning strings (for example, by replacing that DOM parser by a streaming one)

Problem 3: if you’re writing a library, you can’t know anything about the application that will use your code, so you have to guess whether interning some of the strings you create will help the user.

And of course, that’s historically. (Some) modern JVMs already deduplicate strings.

Re:JVM Anatomy Quark #10: String.intern (2019) by GlitchMr on Wednesday June 22, 2022 @03:36PM (#31840799)
I don't think using String::intern makes sense, especially now that Java's garbage collector is capable of deduplicating strings (https://openjdk.org/jeps/192). In the past it could have been used to reduce memory usage when a given string was used a lot, but now there are better ways of dealing with that issue.
Re:JVM Anatomy Quark #10: String.intern (2019) by hyperpape on Wednesday June 22, 2022 @04:27PM (#31841458)
G1's deduplication is nice, but note that G1's deduplication is a lot weaker than what String.intern does. G1 deduplicates the underlying byte array, but leaves separate strings (so s1 == s2 will evaluate false). So you still have an extra object header.

If you have (like one our applications did) millions of copies of the string "USA" in memory, that's many megabytes of memory that explicit deduplication can save that the garbage collector can't.

String.intern isn't the way, for all the reasons this post outlines, but just using G1 isn't the right approach either.

Re:JVM Anatomy Quark #10: String.intern (2019) by vips7L on Wednesday June 22, 2022 @05:37PM (#31842176)
IIRC all of the concurrent GCs can dedupe now. Not just G1.

Hopefully soon object headers will be negligible with progress from Lilliput though.

Re:JVM Anatomy Quark #10: String.intern (2019) by hyperpape on Wednesday June 22, 2022 @05:56PM (#31842375)
Strings have an object header, an int for the hashcode and a pointer to the array. Assuming a < 32 GB heap (so 4 byte pointers), that's 24 bytes for the string, even once the array is deduped. Lilliput is awesome, but an 8 byte header would only reduce that to 16 bytes.
Re:JVM Anatomy Quark #10: String.intern (2019) by jlmorton on Wednesday June 22, 2022 @04:36PM (#31841551)
One common thing I've seen is an attempt to de-duplicate a poorly-crafted cache.

Let's say you have a cache with Map<Language, BusinessInfo>. After a while, the cache is quite large, and you want to reduce its memory usage. You realize within BusinessInfo, you have a MailingAddress, but it's not actually localized. You're duplicating millions of address lines for no reason.

You could split this out of your cache, but pulling that thread is a bit tricky. Instead, you decide to store addressLine.intern().

Re:JVM Anatomy Quark #10: String.intern (2019) by Andaith on Thursday June 23, 2022 @12:10AM (#31845072)
It allows you to do this:

synchronised(bob.intern()) { //This block executes in parallel for requests with different values of bob, but sequentially for each request that has the same value of bob. }

If I didn't have to integrate with a weird 3rd party API where this was necessary, I wouldn't know about String.intern()...

Re:JVM Anatomy Quark #10: String.intern (2019) by kmonsen on Wednesday June 22, 2022 @11:43PM (#31844961)
I think using == for strings is a mistake even if they are interned, except of course in some very controlled circumstances internally in a library if it gives a performance benefit. I don't want to look at my code and need to know if I string is interned to know if it is correct.
Re:JVM Anatomy Quark #10: String.intern (2019) by derriz on Thursday June 23, 2022 @07:11AM (#31847458)
I've seen it used in an old code base so that a specialized Map implementation could be used with String keys.

I'm not even sure of the name of the library it came from or what it was called - maybe "ObjectHashMap" or something like that - but its semantics were that it used reference equality instead of the structural equality used by a regular HashMap. As a result map operations depended on base Object.hashCode and reference equality which are constant and presumably very quick operations.

Using a regular HashMap, the map operations would depend on String.hashCode() and String.equals() which are O(n) in the length of the String - although the return value from hashCode is cached.

On reflection and this was an old code-base, I'm somewhat sceptical that this was a win given that String keyed-HashMaps are so common that I'd imagine a lot of JVM optimisation effort has gone into this area. I guess it would depend on the pattern of use. If the universe of String keys was known in advance, then I guess it operated like a poor mans perfect hash. On the other hand, this is the kind of optimization in Java which I've found to be unstable with performance varying significantly between JVM versions and platforms.

Re:JVM Anatomy Quark #10: String.intern (2019) by chrisseaton on Wednesday June 22, 2022 @04:12PM (#31841283)
> Anything else other than the ability to reliably use == for String comparison instead of .equals()?

Sometimes that's key.

Why not use an int or some other token? Well then you have to garbage collect yourself. Maybe use your own class for the token though instead of a string? But then you still have to do half the garbage collection, as you need a weak map for lookup and possibly some cleaning infrastructure.

Re:JVM Anatomy Quark #10: String.intern (2019) by jillesvangurp on Wednesday June 22, 2022 @10:56PM (#31844683)
The appropriate way to think of it is as an optimization that you will almost certainly never need. If you do, you'd typically confirm that by profiling your code. And it would merely be one of several other strategies that you could employ to save a bit of memory and cpu. I'm sure there are some niche domains where every ms of performance matters enough that you'd go through a lot of trouble for very marginal gains in performance. But otherwise, this should not be something you'd ever need.
Re:JVM Anatomy Quark #10: String.intern (2019) by catp on Wednesday June 22, 2022 @05:00PM (#31841800)
I've used String.intern() in genetic data analysis to reduce memory usage by a large factor. Lots of repeated Strings like "AA", "AB", "BB", etc.
Re:JVM Anatomy Quark #10: String.intern (2019) by monkeybutton on Wednesday June 22, 2022 @05:26PM (#31842068)
I used it a long time ago in a program that read large structured text files. What was 100s of MB of text in memory became a fraction in size after interning the tokens.
Re:JVM Anatomy Quark #10: String.intern (2019) by jefffoster on Wednesday June 22, 2022 @03:35PM (#31840781)
Once upon a time I found string interning useful in conjunction with IdentityHashMap for quick lookup of lots of strings.