Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strings in java 8 less memory

I got below code and I was asked which option gets the following pattern:

XXXX-XXXX-XXXX-2324

...

Code Below:

public class CCMark {

    public static String maskCC(String creditCard){

        String x = "XXXX-XXXX-XXXX-";
        //line 1
    }

    public static void main(String[] args) {
        System.out.println(maskCC("1234-5678-1234-2324"));
        System.out.println(maskCC("4567-5678-1234-5643"));
        System.out.println(maskCC("1234-5678-1234-4654"));
        System.out.println(maskCC("4567-5678-1234-5435"));
    }

}

Below possible options that can be inserted on "line 1":

    A) 
    return x + creditCard.substring(15, 19);

    B) 
    StringBuilder sb = new StringBuilder(x);
    sb.append(creditCard, 15, 19);
    return sb.toString();

I think that the best option here, as A and B provide us with the same output, is B, because it is using StringBuilder which means that its approach is mutable, so it will use less memory than option A.

Am I wrong? Could it be that option A for this particular situation is the best option?

like image 548
Cuban coffee Avatar asked Nov 29 '15 10:11

Cuban coffee


People also ask

Does string makes Java more memory efficient?

To make the java more memory efficient the concept of string literal is used. By the use of 'new' keyword, the JVM will create a new string object in the normal heap area even if the same string object present in the string pool.

How much memory do strings take up Java?

An empty String takes 40 bytes—enough memory to fit 20 Java characters. The results clearly show that a String 's memory growth tracks its internal char array's growth. However, the String class adds another 24 bytes of overhead.

Does string take more memory than int?

strings take up more memory, but the memory size is tiny... an integer takes up about 4 bytes for its actual data (some extra for memory pointers depending on how you store it, but string will have that same extra memory as well). A string takes up about a byte per letter (with a minimum of course as .

How do I reduce Java memory usage?

Set the Heap Size If you reduce the Java heap size by a certain amount you will reduce the memory footprint of the Java process by the same amount. You can however not reduce the Java heap size infinitely. The heap must be at least large enough for all objects that are alive at the same time.


2 Answers

Options a and b are identical, because the Java compiler will convert option a into option b. You could move the declaration of x outside the method (and make it final). Something like,

static final String x = "XXXX-XXXX-XXXX-";
public static String maskCC(final String creditCard) {
    return x + creditCard.substring(15, 19);
}

Using javap to check the first against, the second. Java code like,

String x = "XXXX-XXXX-XXXX-";
String creditCard = "1234-5678-1234-23324";
String x2 = x + creditCard.substring(15, 19);
StringBuilder sb = new StringBuilder(x);
sb.append(creditCard, 15, 19);
String x3 = sb.toString();

generates byte-code that looks like (note lines 6-31 and 32-58)

 0: ldc           #16                 // String XXXX-XXXX-XXXX-
 2: astore_1
 3: ldc           #18                 // String 1234-5678-1234-23324
 5: astore_2
 6: new           #20                 // class java/lang/StringBuilder
 9: dup
10: aload_1
11: invokestatic  #22                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
14: invokespecial #28                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
17: aload_2
18: bipush        15
20: bipush        19
22: invokevirtual #31                 // Method java/lang/String.substring:(II)Ljava/lang/String;
25: invokevirtual #35                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: invokevirtual #39                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
31: astore_3
32: new           #20                 // class java/lang/StringBuilder
35: dup
36: aload_1
37: invokespecial #28                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
40: astore        4
42: aload         4
44: aload_2
45: bipush        15
47: bipush        19
49: invokevirtual #43                 // Method java/lang/StringBuilder.append:(Ljava/lang/CharSequence;II)Ljava/lang/StringBuilder;
52: pop
53: aload         4
55: invokevirtual #39                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
58: astore        5
60: return
like image 165
Elliott Frisch Avatar answered Oct 24 '22 10:10

Elliott Frisch


The big advantage of the variant A, return x + creditCard.substring(15, 19); is that it is simple and clean and it works in all Java versions from 1 to 8. In the case that its compiled form uses StringBuffer, a simple recompile for Java 5 or newer will make it use StringBuilder instead. This flexibility is lost when you work with either, StringBuffer or StringBuilder, manually.

The exact compiled form is not fixed. Since the semantic of the method String.substring is not fixed by the Java Language Specification, compilers usually won’t touch this and compile it as an ordinary method invocation. The specification encourages compiler vendors to use StringBuilder for string concatenation (the + operator) whenever there is a benefit and most compilers will do so, even when there is no benefit. Here, both, x and the result of substring, are Strings so a simple String.concat would be simpler but most compilers always use StringBuilder, compiling variant A to the equivalent of
return new StringBuilder().append(x).append(creditCard.substring(15, 19)).toString();.

Comparing this typical form with your variant B, we can conclude that variant B has two advantages performance-wise:

  • new StringBuilder(x) initializes the StringBuilder to a capacity of x.length()+16 which is sufficient for the entire operation, whereas the default capacity of new StringBuilder(), typically used for variant A, is fixed to 16 characters which misses the mark here as we have a result of 19 characters, thus a reallocation and copying of the underlying character array will occur

  • sb.append(creditCard, 15, 19); will copy the four characters without the need to create an intermediate String representation of these characters. The expenses of the substring operation differ depending on the implementation, e.g. in Oracle’s implementation there was a significant change with version 1.7.0_06; starting with this version a substring requires a new char[] array holding a copy of the affected character data as it doesn’t maintain a separate offset and length field

But note that all these differences of variant A and B only affect the formal description of the operation to perform. What will actually happen, is up to the JVM/JRE and usually the Hotspot optimizer knows a lot of string related operations and may fuse operations or elide intermediate string representations. Thus, the outcome regarding performance is rather unpredictable and may be affected by subtle changes to the implementation.

That’s why developers might stick to variant A which is, as said, simpler and more readable, and only care for performance once a profiler tells them that there is a performance problem that could be solved by dealing with Stringbuilder manually.

like image 41
Holger Avatar answered Oct 24 '22 10:10

Holger