Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strings and Memory Management

I am having a hard time trying to understand what is going on with Strings and memory management in Android (Java).

To simplify, have a look at this simple piece of code:

public void createArray(){
        String s = "";
        String foo = "foo";
        String lorem = "Lorem ipsum ad his scripta blandit partiendo, eum 
                 fastidii accumsan euripidis in, eum liber hendrerit an.";
        ArrayList<Item> array = new ArrayList<Item>();

        for( int i=0; i<20000; i++ ){
            s = foo.replace("foo", lorem);
            array.add( new Item(s) );
        }
        System.gc();
    }

private class Item{
    String name;

    public Item(String name){
        this.name = name;
    }
}

If executed, createArray will allocate more than 5Mb in memory. Even with the System.gc() instruction, memory won't be deallocated after the loop.

Now, if we replace s = foo.replace("foo", lorem); with s = lorem;, allocated memory will only increase by 0.5Mb.

I need to understand what is going on to improve my application's performance.

Can anybody explain how should I replace Strings in a case like this? And why is System.gc() not deallocating memory?

Thanks.

UPDATE:

Thanks for the answers, now I understand that System.gc() is only a hint.

To clarify the other question (the important one):

How can I dynamically generate 20000 Strings ("foo1", "foo2"... "foo20000") add them to the ArrayList and do not run out of memory? If this 20000 strings were static they wouldn't allocate more than 0.5Mb in memory. Besides, if s = foo.replace("foo", lorem) creates a brand new String, why my function allocates 5Mb which is 10 times more memory? Shouldn't be around 1Mb?

(I am already thinking in a workaround but I want to be sure there is no way to generate the strings dynamically without using this huge amount of memory)

like image 280
Xavi Gil Avatar asked Aug 08 '11 15:08

Xavi Gil


4 Answers

replace is generating a brand new String object every time it is called, since strings in Java are immutable and so modifications cannot be made "ìn-place". Also, you're adding that String to an ArrayList which will hold a reference to the new object, preventing it from being collected.

like image 96
dlev Avatar answered Oct 16 '22 10:10

dlev


Because String's are immutable, calling foo.replace("foo", lorem) will create a new String each time. In the example where you simply set s = lorem, no new String is created.

Also, System.gc() is simply a recommendation to the VM, and it will in no way guarantee a garbage collection. There is nothing you can do to force a garbage collection. (other than using up all available memory that is)

like image 35
nicholas.hauschild Avatar answered Oct 16 '22 11:10

nicholas.hauschild


System.gc() is a hint to the garbage collector to run again, when it has the time.

That said, the garbage collector only collects unreferenced objects, and all the strings you generated are still being held by the object array. It is entirely possible that you might add the lines after System.gc()

for (String item : array) {
   System.out.println(item);
}

Which would have proven the garbage collector's wisdom in not destroying the strings.

In the event that you have some true need to reuse a String, you can trade off CPU cycles for possibly lower memory footprint with String intern methods (which will scan allocated strings for duplicates and return a reference to the single kept string if found.

You might argue that in your case the garbage collector hint could invoke compile time optimization techniques to realize that array is not being used downstream and thus act to destroy the strings before they would normally be dereferenced at the end of the program; however, it would add side effects that would appear quite strange in some runtime environments (like debugging sessions).

In the end, it is not possible to accurately perform reflection, code stepping, compile binding to source code, and various types of reflection with such exotic compile time optimization. That said, Java optimizes itself pretty well considering how many features you get out-of-the-box.

like image 28
Edwin Buck Avatar answered Oct 16 '22 10:10

Edwin Buck


Keep in mind that System.gc() does not force the garbage collector to run. It is merely a hint that you would like it to run at some point in the future.

like image 1
Erich Douglass Avatar answered Oct 16 '22 09:10

Erich Douglass