липса на памет при преобразуване на голям поток в низ

Опитвам се да конвертирам голям поток (4mb) в низ, който в крайна сметка го конвертирам в JSON масив.

когато размерът на потока е малък (в KB), всичко работи добре, в момента, в който започне да обработва 4mb потока, паметта му свършва

по-долу е това, което използвам, за да конвертирам потока в низ, опитах почти всичко и подозирам, че проблемът е с цикъла while. може ли някой да помогне?

  public String convertStreamToString(InputStream is)
            throws IOException {

        if (is != null) {
            Writer writer = new StringWriter();

            char[] buffer = new char[1024];
            try
            {
                Reader reader = new BufferedReader(
                        new InputStreamReader(is, "UTF-8"));
                int n;
                while ((n = reader.read(buffer)) != -1) 
                {
                    writer.write(buffer, 0, n);
                }
            }
            finally 
            {
                is.close();
            }
            return writer.toString();
        } else {       
            return "";
        }
    }

Актуализация: добре, дотук стигнах в момента, на прав път ли съм? Мисля, че съм близо.. не съм сигурен какво друго мога да затворя или да изчистя, за да си възвърна паметта..

public String convertStreamToString(InputStream is)
        throws IOException {

    String encoding = "UTF-8";
    int maxlines = 2000;
    StringWriter sWriter = new StringWriter(7168);
    BufferedWriter writer = new BufferedWriter(sWriter);
    BufferedReader reader = null;
    if (is == null) {
        return "";
    } else {     


        try {
            int count = 0;
            reader = new BufferedReader(new InputStreamReader(is, encoding));
            for (String line; (line = reader.readLine()) != null;) {
                if (count++ % maxlines == 0) {
                    sWriter.close();
                    // not sure what else to close or flush here to regain memory
                    //Log.v("Max Lines Reached", "Max Lines Reached");;
                }

                writer.write(line);


            }
            Log.v("Finished Loop", "Looping over");


    } finally {
        is.close();
        writer.close();

    }
        return writer.toString();
    }
}

person Leon Leony    schedule 22.12.2012    source източник
comment
Най-напред: защо не обърнете условието if в началото? if (is == null) return ""; Освен това опитайте да използвате Jackson: той има API за поточно предаване за преобразуване на входни потоци в JSON.   -  person fge    schedule 22.12.2012
comment
Разгледах Джаксън и GSON, опитвайки се да ги избегна, тъй като те включват повече работа... това работеше добре, докато потокът не стана твърде голям, за да се справи.   -  person Leon Leony    schedule 22.12.2012
comment
Защо не използвате StringBuilder вместо StringWriter. Но не съм сигурен дали това решава проблема с паметта ви.   -  person MrSmith42    schedule 22.12.2012
comment
Или трябва да увеличите наличната памет за JVM с настройката -Xmx или ... не зареждайте всичко в паметта.   -  person Brian Roach    schedule 22.12.2012
comment
@LeonLeony: повече работа? Джаксън е много прост! Създайте ObjectMapper, използвайте неговите методи за четене и това е всичко.   -  person fge    schedule 22.12.2012
comment
това е проект за android, разгледах възможността за увеличаване на купчината, но очевидно не помага наистина с android или изглежда не работи.   -  person Leon Leony    schedule 22.12.2012
comment
Когато използвате Stringwriter и го конвертирате в низ, използвате почти два пъти повече памет. Защо не използвате променливи класове низове, докато изграждате низ извън потока. Също така можете да изградите JSON масив, докато четете през steam   -  person coder000001    schedule 22.12.2012


Отговори (1)


StringWriter пише на StringBuffer вътрешно. StringBuffer е основно обвивка около char масив. Този масив има определен капацитет. Когато този капацитет е недостатъчен, StringBuffer ще разпредели нов по-голям char масив и ще копира съдържанието на предишния. Накрая извиквате toString() на StringWriter, който отново ще копира съдържанието на char масива в char масива на резултантния String.

Ако имате някакъв начин да знаете предварително какъв е необходимият капацитет, трябва да използвате конструктора на StringWriter, който задава първоначалния капацитет. Това ще избегне ненужното копиране на масиви за увеличаване на буфера.

И все пак това не избягва окончателното копие, което се случва в toString(). Ако имате работа с потоци, които могат да бъдат големи, може да се наложи да преосмислите дали наистина имате нужда от този входен поток като String. Директното използване на достатъчно голям масив char ще избегне копирането наоколо и ще намали значително използването на паметта.

Крайното решение би било да се извърши част от обработката на входа, преди целият вход да е влязъл, така че знаците, които са обработени, да могат да бъдат отхвърлени. По този начин ще трябва да държите в паметта само толкова, колкото е необходимо за стъпка на обработка.

person bowmore    schedule 22.12.2012
comment
благодаря ви.. току-що тествах това, също така удвоих капацитета, не помогна.. не мога да изляза от цикъла while без срив при липса на памет. - person Leon Leony; 22.12.2012
comment
нов StringWriter(1024) и нов StringWriter(5120) - person Leon Leony; 23.12.2012
comment
Ако имате 4 MB данни, те няма да се поберат в 5120 знака. Вътрешният буфер все още ще трябва да се увеличи. И ако сте прочели отговора ми внимателно, ще забележите, че правилната инициализация на вътрешния буфер решава само половината от проблема. - person bowmore; 23.12.2012
comment
да, благодаря.. прочетох внимателно отговора ви, разбирам, че това решава половината от проблема. Опитах се да увелича буфера до 102400, но все пак .. без успех .. - person Leon Leony; 23.12.2012
comment
Затварянето на StringWriter няма ефект. И в крайна сметка все още имате целия вход в StringWriter и го копирате в String =› използване на паметта * 2. Наистина ли имате нужда от String? Целият низ? - person bowmore; 23.12.2012
comment
да, същият низ след това се преобразува в jsonarray obj = new JSONArray(thestring); - person Leon Leony; 23.12.2012
comment
Можете да помислите за анализиране на елементите на масива поотделно. Или, ако вие сте този, който пише оригиналния поток, помислете за писане на отделните елементи. - person bowmore; 24.12.2012