Vraag Hoe efficiënt geheugen beheren met behulp van strings?


Beschouw een voorbeeldcode.

public void testString()
{     
    int i = 0;
    while(i < 100000000)
    {
        String s ="Hi hello bye" +i;
        i++;          
    }
}

In elke iteratie wordt een nieuwe tekenreeks gemaakt en de waarde ervan is niet langer nodig voor de volgende iteratie. Ik heb geprobeerd het vooraf verbruikte geheugen te printen en deze testString () -functionaliteit te posten. Dit zijn hun waarden.

Before invoking testString():

Total Memory: 91684864 (87.4375 MB)
Max Memory:   1360855040 (1297.8125 MB)
Free Memory:  72163552 (68.82052612304688 MB)

After invoking testString():
Total Memory: 424280064 (404.625 MB)
Max Memory:   1360855040 (1297.8125 MB)
Free Memory:  171766816 (163.80960083007812 MB).

Ik zie een grote hoeveelheid geheugen worden gebruikt en ben bang dat JVM Heap buiten de grenzen kan gaan vanwege de huidige manier van omgaan met strings. De reeks die is gegenereerd voor iteratie 1 is niet langer nodig in iteratie 2 en de opslagruimte kan worden vrijgegeven. Ik geloof dat dat hier niet gebeurt.

Ik heb geprobeerd met behulp van StringBuffer en StringBuilder-objecten en er lijkt een zeer marginale verbetering in het geheugengebruik.

Help mij alstublieft een betere en optimale aanpak.


24
2018-04-30 05:32


oorsprong


antwoorden:


De reeks die is gegenereerd voor iteratie 1 is niet langer nodig in iteratie 2 en de opslagruimte kan worden vrijgegeven. Ik geloof dat dat hier niet gebeurt.

Het zeker is gebeurt.

U maakt 100 miljoen reeksen waarvan elk ten minste 13 tekens bevat en waarvan de meeste ongeveer 20 tekens lang zijn. Elke string bestaat uit een object (met overhead) en een char[] - dus ik denk dat het ongeveer 60 bytes kost voor een reeks van 20 tekens.

Als het verzamelen van garages niet effectief was, zouden voor 100 miljoen objecten die elk 60 bytes vereisen 6 GB zijn - terwijl u een totaalgeheugen ziet dat slechts ongeveer 300 MB groter is dan waarmee het begon.

De touwtjes zijn worden verzameld - alleen niet per direct.

Je hebt ons niet verteld wat je moet doen met de snaren in je echt code (ik neem aan dat er een echte motivatie voor is) - ervan uitgaande dat je eigenlijk een string nodig hebt in elke iteratie van een lus, denk ik niet dat StringBuilder gaat je helpen. als jij enkel en alleen nodig hebben de gegevens is een StringBuilder dan kun je het een stuk efficiënter maken, maar het is zeldzaam dat je een StringBuilder maar bel niet toString ben ermee bezig.


49
2018-04-30 05:43



Wat zal er gebeuren tijdens de eerste run

JVM voert de code uit, genereert de tekenreeksen en op gezette tijden maakt de Garbage Collector het gebruikte geheugen vrij. Naast wat verspilde uitvoeringstijd, zal het programma normaal werken.

Wat gebeurt er als de functie vaak wordt aangeroepen

JVM begint met het optimaliseren van de lus, realiseert zich dat niets ooit met die strings is gedaan en markeert de volledige functie als dode code. Uiteindelijk zal het aanroepen van de functie letterlijk niets doen, omdat de JVM de inhoud geconverteerd heeft naar een simpele return

Help mij alstublieft een betere en optimale aanpak.

Omdat zelfs de JVM geen idee heeft wat uw code moet doen ... wat wilt u doen? Er is misschien een optimale oplossing voor uw actuele probleem bij de hand, dat is heel anders dan het codevoorbeeld dat u in eerste instantie hebt gepost.


7
2018-04-30 11:28



Het hangt af van hoe je StringBuilder hebt gebruikt. Deze

    StringBuilder sb = new StringBuilder("");
    while (i < 100000000) {
        sb.delete(0, sb.length());
        sb.append("Hi hello bye").append(i);
        i++;
    }

zal veel efficiënter zijn, zowel in geheugengebruik als snelheid


3
2018-04-30 05:43



String is onveranderlijk, wanneer u een string continu toevoegt die een stringobject maakt voor elke iteratie, dus het geheugen is overschreden. als u StringBuilder gebruikt, werkt het in een enkel object, zelfs als er meerdere iteraties plaatsvinden. StringBuilder kan worden gewijzigd.

StringBuilder iter = new StringBuilder("");
while (i < 100000000) 
{
    iter.delete(0, iter.length());
    iter.append("Hi hello bye").append(i);
    i++;
} 

3
2018-04-30 05:49



De JVM mag nooit het heap-geheugen van niet-verwezen objecten bevatten, zoals de tekenreeksen in uw voorbeeldprogramma, omdat voordat het een OutOfMemory uitzondering, het zal garbage collection uitvoeren. Zie de semantiek van deze uitzondering van de Java Virtual Machine Specification, paragraaf 6.3:

OutOfMemoryError: de Java Virtual Machine-implementatie heeft geen virtueel of fysiek geheugen meer en de automatische opslagmanager kon niet genoeg geheugen terugwinnen om aan een aanvraag voor het maken van een object te voldoen.


3
2018-04-30 11:13



Het gebruik van stringbuilders is de beste optie die je hebt. Omdat het aantal strings / stringbuilders dat je nodig hebt enorm is, kun je niet verwachten dat JVM in feite vrijwel het gebruik van een groot deel van het geheugen zal vermijden. Verwijzend naar uw statistieken hierboven:

Met gebruik van string:% vrij geheugen op totaal geheugen is 40.48% ((163.80960083007812 MB/404.625 MB )*100).

Met gebruik van stringbuilders:% van het vrije geheugen op het totale geheugen is 69.35 % ((252.659 MB/364.3125 MB)*100), wat een behoorlijk significante verbetering is. Het gebruik van de bovenstaande variabele bevindt zich ook alleen binnen het bereik van de lus, daarom zal de vuilnisophaler van JVM worden uitgevoerd om het geheugen te wissen als dit eenmaal is vereist.


3
2018-04-30 12:23



Omdat het bereik van de variabele alleen bedoeld is voor de iteratie van de while-lus, hoef je je hier dus geen zorgen te maken over de geheugenoverloop, want bij de volgende uitvoering van Garbage Collector wordt het geheugen leeggemaakt:

while(i < 100000000)
{
    String s ="Hi hello bye" +i;
    i++;          
}// no more required the s afterward

In elke iteratie maakt String s een nieuw object en de vorige is nu niet nodig, dus het bevindt zich in het geheugen totdat de garbage collector het opruimt.


2
2018-04-30 06:48