27258


InputStreamをStringに変換しようとするとOutOfMemoryErrorが発生します。  
変換しようとしているInputStreamはAndroidのプロジェクトのrawフォルダの中にあるバイナリファイル(5.5MB)を次のようにInputStreamに変換したものです。  
OoMがスローされないよう改善できませんか？

[code]

    InputStream is = this.getRawResouces().openRawResouce();
    
[/code]

メモリが876MBの環境でテストしています。

# ソースコード

## readメソッドを使って書いたコード

[code]

    static String convertInputStreamToString(InputStream is) throws IOException {
    InputStreamReader reader = new InputStreamReader(is);
    StringBuilder builder = new StringBuilder();
    char[] buf = new char[512];
    int numRead;
    while (0 <= (numRead = reader.read(buf))) {
        builder.append(buf, 0, numRead);
    }
        buf=null;
    return builder.toString();//ココでOoM発生
    } 
    
[/code]

# readLine

[code]

    static String inputStreamToString(InputStream is){
        BufferedReader reader = null;
        try{
            reader = new BufferedReader(new InputStreamReader(is,"UTF-8"));
        }
        catch (UnsupportedEncodingException e){
            e.printStackTrace();
        }
        StringBuilder sb = new StringBuilder();
        String b = null;
        try{
            while ((b=reader.readLine()) !=null){
                sb.append(b);
            }
        }
        catch (IOException e){
    
        }
        return sb.toString();
    }
    
[/code]

## 呼び出し元のコード

[code]

     private void checkVersion() throws Throwable{
        //呼び出し元は、onResume()のonClick()
        InputStream versionStream=this.getResources().openRawResource(R.raw.git);
    
        String verchecked=Event.convertInputStreamToString(versionStream);
        //ココでreadメソッドを使って書いたコードを使って変換しようとするが、OoM発生、スローされる
        versionStream.close();//closeは書いている
        java.lang.Process p = null;
        TextView vertext=(TextView) findViewById(R.id.version);
    
        try{
            p=Runtime.getRuntime().exec(verchecked+" version");
            verchecked=null;//vercheckedはもう使わないからnull
            InputStream returnVerison=p.getInputStream();//結果を返す
            p=null;//pはもう使わないからnullを代入
            String ver=Event.convertInputStreamToString(returnVerison);
            returnVerison.close();
            vertext.setText(ver);
            ver=null;
        }catch (IOException e){
            e.printStackTrace();
            vertext.setText("取得失敗: "+e);
        }
        vertext=null;
    }
    
     protected void onResume(){
            super.onResume();
          //このクラスはMainActivity(最初に呼び出されるActivity)から呼び出される
            Button Update=(Button) findViewById(R.id.UpdateButton);
            Update.setOnClickListener(new OnClickListener(){
                    @Override
                    public void onClick(View p1){
                        try{
                            checkVersion();
                        }catch(OutOfMemoryError e){
                            e.printStackTrace();
                            Toast.makeText(GitUpdate.this,"取得失敗:"+e,Toast.LENGTH_SHORT).show();
                        }
                    }
            });
    
[/code]

# ヒープ

Android StudioでMemoryの様子を見てみました。一応画像貼っておきます。  
![](https://i.stack.imgur.com/wDbGd.jpg)  
へこんでいるところがInputStreamを変換したところです。  
MB単位です。  
`totalMemory(),MaxMemory()`  
7 ,96

# スタックトレース

[code]

    06-22 20:06:54.681 W/System.err(28046): java.lang.OutOfMemoryError
    06-22 20:06:54.681 W/System.err(28046):     at java.lang.AbstractStringBuilder.enlargeBuffer(AbstractStringBuilder.java:94)
    06-22 20:06:54.681 W/System.err(28046):     at java.lang.AbstractStringBuilder.append0(AbstractStringBuilder.java:145)
    06-22 20:06:54.681 W/System.err(28046):     at java.lang.StringBuilder.append(StringBuilder.java:216)
    06-22 20:06:54.681 W/System.err(28046):     at java.lang.ProcessManager.exec(ProcessManager.java:211)
    06-22 20:06:54.681 W/System.err(28046):     at java.lang.Runtime.exec(Runtime.java:173)
    06-22 20:06:54.681 W/System.err(28046):     at java.lang.Runtime.exec(Runtime.java:246)
    06-22 20:06:54.681 W/System.err(28046):     at java.lang.Runtime.exec(Runtime.java:189)
    06-22 20:06:54.681 W/System.err(28046):     at com.jimdo.solarand.git.GitUpdate.checkVersion(GitUpdate.java:153)
    06-22 20:06:54.681 W/System.err(28046):     at com.jimdo.solarand.git.GitUpdate.access$1000009(GitUpdate.java)
    06-22 20:06:54.681 W/System.err(28046):     at com.jimdo.solarand.git.GitUpdate$100000001.onClick(GitUpdate.java:50)
    06-22 20:06:54.681 W/System.err(28046):     at android.view.View.performClick(View.java:4487)
    06-22 20:06:54.681 W/System.err(28046):     at android.view.View$PerformClick.run(View.java:18746)
    06-22 20:06:54.691 W/System.err(28046):     at android.os.Handler.handleCallback(Handler.java:733)
    06-22 20:06:54.691 W/System.err(28046):     at android.os.Handler.dispatchMessage(Handler.java:95)
    06-22 20:06:54.691 W/System.err(28046):     at android.os.Looper.loop(Looper.java:149)
    06-22 20:06:54.691 W/System.err(28046):     at android.app.ActivityThread.main(ActivityThread.java:5257)
    06-22 20:06:54.691 W/System.err(28046):     at java.lang.reflect.Method.invokeNative(Native Method)
    06-22 20:06:54.691 W/System.err(28046):     at java.lang.reflect.Method.invoke(Method.java:515)
    06-22 20:06:54.691 W/System.err(28046):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:788)
    06-22 20:06:54.691 W/System.err(28046):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:604)
    06-22 20:06:54.691 W/System.err(28046):     at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:132)
    06-22 20:06:54.691 W/System.err(28046):     at de.robv.android.xposed.XposedBridge.main(Native Method)
    06-22 20:06:54.691 W/System.err(28046):     at dalvik.system.NativeStart.main(Native Method)
    
[/code]

# その他情報

解決を早めるために、[別サイト](https://teratail.com/questions/38726)にも投稿させて頂きました。  


CC BY-SA 3.0
2016-05-14T02:48:43.797
24842
2016-07-01T22:25:07.747
2016-06-22T11:17:01.380
15185
15185
question
android java
AndroidでInputStreamをStringに変換しようとするとOOMが発生する


readlineがいけないのかもしれません。  
別方法のread等を使ってみてください。  
readlineの対象の１行が巨大だった場合、読込時に連続した領域を確保できなくてＯＯＭが発生している可能性が考えられます。


CC BY-SA 3.0
2016-05-18T00:06:28.343
24949
2016-05-18T00:06:28.343
12233
24842
answer


もし5.5MBのサイズのファイルのすべての文字列が一行としてreadLineで読み込まれるのだとしたら、まずb:Stringにはおそらく11MBのヒープ確保が必要になります。  
またStringBuilderのappendではchar配列に文字列を差し込む処理で一時的に、charとString双方を確保するために、サイズが2倍必要になっているのかもしれません（すみません、nativeのコードを見ましたがそこまで把握できず/
[たぶんここらへん](https://android.googlesource.com/platform/art/+/master/runtime/native/java_lang_String.cc)）

仮に22MBそこで必要になっているのだとしたら、上の画像にある75MBから空白Heap含めた約95MBを上回ってしまっているのでは？という仮定はできると思います。  
ただ上記の画像では落ちてないっぽいので、この仮定の前提が違う気がしますけど。

※というか画像のonResumeにsleep書いてるのやばい気がしますけど…。もっと他に何かあるだろうという気がしてきますね…。


CC BY-SA 3.0
2016-05-18T02:19:13.550
24954
2016-05-18T02:19:13.550
15237
24842
answer


結論が出ました。バイナリデータを文字列に変換するのは **無理** です。  
むりやり文字列に変換すると、1バイトのバイナリデータがで2、3バイト程の文字と認識される場合があり、二倍、三倍くらいのヒープが消費されるためです。  
272shin16さんが言うには

> 最低でもヒープサイズが11MBは必要になるということには変わりないです。

とのことです。teratailでも

> バイナリデータを無理やり文字に変換しようとしていますから、  
>  1バイトのバイナリデータが2バイトの文字に変換される場合があり、最悪2倍のメモリを消費する結果となっているのではないかと思われます。

とのことです。  
これでは空白ヒープを上回り、最終的にはOoMが発生という流れでしょう。


CC BY-SA 3.0
2016-07-01T22:25:07.747
27258
2016-07-01T22:25:07.747
15185
24842
answer