0

I've got an incoming bunch of MultipartFile objects that I need to send through by embedding them into an XML structure and base64-encoding that structure. Initially the code easily consumed 10-20x that of the size of the files. VisualVM showed that this was due to many copies of the files being present in the form of byte arrays and Strings.

An example cause, consider the following line of code to base64 encode String filesXML which represents the XML with the files embedded in it :

Base64.encoder().encodeAsString(filesXML.getBytes())

This easily consumes 5x the size of the XML in terms of memory. Because getBytes() does a copy and the encodeAsString() method uses a StringBuilder internally, which apparently easily uses up 4x the size of the byte array it is encoding.

So I adapted code to start working with byte arrays myself and filling them in chunk by chunk, e.g. to construct the XML or to encode it in base64 format. By initializing StringBufferor StringBuilder objects with capacities I know beforehand, I avoid lots of duplication. However I cannot find a way to for example replace things within a String without causing copying.

Is there a way to work with a String in-place? Replace parts of it without creating anything new? Create a String based on a byte array without actually copying that array? Etc.

Bruno
  • 63
  • 7
  • 3
    No. The Java class `String` is immutable. There is no way to modify the content of an existing `String` object. – Jesper May 20 '22 at 14:22
  • There's not even a way to do `new String(array)` given some byte array without causing a clone of that array? I'd have to carry around all my primitive `char` or `byte` arrays instead then? – Bruno May 20 '22 at 14:24
  • 1
    Class `String` makes a defensive copy if you create a new `String` from an array, because users should of course not be able to modify the array after the `String` object has been created. If you need a modifiable container of characters, then you'll need to use something else than a `String`. For example, as you've discovered yourself: a `StringBuilder`, or a `char[]`, or a `java.nio.CharBuffer`. – Jesper May 20 '22 at 14:29
  • Ok thank you, guess it wouldn't be properly object-oriented otherwise. – Bruno May 20 '22 at 14:31
  • 1
    `String` is not immutable to make it "properly object-oriented". Here's an explanation: [Why String is Immutable in Java?](https://www.baeldung.com/java-string-immutable) – Jesper May 20 '22 at 14:33
  • 2
    You could use [`String.intern()`](https://stackoverflow.com/questions/10578984/what-is-java-string-interning). Maybe that fits your needs. – Seelenvirtuose May 20 '22 at 15:04
  • Any of you feel free to provide an answer and I'll accept it. Currently I'm going to carry around byte arrays and make my REST API endpoints run sequentially to help avoid OOMErrors (my service is slow and hence a series of calls easily ends up being ran concurrently). @Seelenvirtuose [couldn't that have a significant performance penalty?](https://stackoverflow.com/questions/10624232/performance-penalty-of-string-intern) I don't have many Strings, just a few but large ones since they represent files. – Bruno May 20 '22 at 15:13

0 Answers0