質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

Q&A

0回答

691閲覧

連続ファイル保存時に、AndroidのNativeメモリが増え続ける原因と解決方法について

Wind

総合スコア442

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

0グッド

2クリップ

投稿2018/04/12 13:21

編集2022/01/12 10:55

#問題
イメージ説明

AndroidStudio 3.0.1で定期的にデータを取得するアプリを作っています。
実機はAndroid6.0です。

Android Profilerで使用メモリを見ていると、
アプリ起動直後のNative領域の使用量は10MB程度なのですが、
データ取得後のGCが発生する毎に20~50Byteくらい溜まっていて、
長時間経つと画像の様にNative領域の使用量が大幅に増えてしまいます。

#確認したこと
・「Dump Java heap」を使用して「Arrange by package」にして「com\example\アプリ名」からアプリが使用しているメモリが増えていないことを確認しました。
・HPROFファイルを保存してEclipse Memory Analyzerで読み込んでみましたが、エラーメッセージが表示されて読み込みませんでした。
(ファイルの破損かもと思いましたが、AndroidStudioでは読み込めて表示出来ました。)
・LeakCanaryを入れていますが、検知されませんでした。
finalizeは使用していません。
・CPUの使用率はAndroid Profilerで10%未満です。

#知りたいこと
投げっぱなしになってしまいますが、AndroidのNativeメモリが増え続ける原因が思いつきませんので、調べる方法と解決方法が知りたいです。
もしくは他にやってみた方がいいことだけでも教えていただけないでしょうか。

#【5/9 追記】現象が再現するソースコード
ボタンを押すと、内部ストレージの/testフォルダにファイルを保存し続けます。
下記コードを実行すると、AndroidProfilerのGCボタンを押してもNativeメモリが減りませんが、
ファイル保存時に明示的に開放する物などはありますでしょうか?
※5/10に一部修正し、処理を軽くしても再現することを確認しました。

Java

1public class MainActivity extends AppCompatActivity { 2 3 private static final int REQUEST_EXTERNAL_STORAGE = 1; 4 private static String[] PERMISSIONS_STORAGE = { 5 Manifest.permission.READ_EXTERNAL_STORAGE, 6 Manifest.permission.WRITE_EXTERNAL_STORAGE 7 }; 8 9 public static void verifyStoragePermissions(Activity activity) { 10 int writePermission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE); 11 int readPermission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE); 12 13 if (writePermission != PackageManager.PERMISSION_GRANTED || readPermission != PackageManager.PERMISSION_GRANTED) { 14 ActivityCompat.requestPermissions( 15 activity, 16 PERMISSIONS_STORAGE, 17 REQUEST_EXTERNAL_STORAGE 18 ); 19 } 20 } 21 22 private TextView textView1; 23 private TextView textView2; 24 25 public static final String TAG = "MainActivity"; 26 private String fileName1 = "test1"; 27 private String fileName2 = "test2"; 28 private String strFileName1; // 保存ファイル名(フルパス) 29 private String strFileName2; // 保存ファイル名(フルパス) 30 31 private int intRaw = 0; 32 private int intFileNumber = 0; 33 private boolean bolTimerFlag = false; // タイマー実行フラグ 34 35 @Override 36 protected void onCreate(Bundle savedInstanceState) { 37 super.onCreate(savedInstanceState); 38 setContentView(R.layout.activity_main); 39 verifyStoragePermissions(this); 40 41 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // スマホ画面を自動スリープさせない 42 43 textView1 = findViewById(R.id.text_view1); 44 textView2 = findViewById(R.id.text_view2); 45 46 // ファイル作成ボタン 47 Button buttonSave = findViewById(R.id.button_save); 48 buttonSave.setOnClickListener(new View.OnClickListener() { 49 @Override 50 public void onClick(View v) { 51 if(bolTimerFlag == false) 52 { 53 handler.post(r); // 実行 54 bolTimerFlag = true; 55 Log.i(TAG,"Timer Run"); 56 } 57 else 58 { 59 handler.removeCallbacks(r); // キャンセル 60 bolTimerFlag = false; 61 mediaScan(); // 途中のファイルをメディアスキャン 62 Log.i(TAG,"Timer Stop"); 63 } 64 } 65 66 // 定期的に実行 67 // https://qiita.com/aftercider/items/81edf35993c2df3de353 68 final Handler handler = new Handler(); 69 final Runnable r = new Runnable() { 70 @Override 71 public void run() { 72 73 strFileName1 = Environment.getExternalStorageDirectory().getPath() + "/test/" + fileName1 + " " + String.format("%04d",intFileNumber) + ".csv"; // 内部ストレージ直下のtestフォルダ 74 strFileName2 = Environment.getExternalStorageDirectory().getPath() + "/test/" + fileName2 + " " + String.format("%04d",intFileNumber) + ".csv"; // 内部ストレージ直下のtestフォルダ 75 76 String text = "test" + intRaw + ","; // 保存する内容 77 StringBuilder sb = new StringBuilder(); 78 if(intRaw % 2 == 0) 79 { 80 saveFile(strFileName1, text); 81 sb.append("Saved:").append(intRaw).append("偶数"); 82 }else 83 { 84 saveFile(strFileName2, text); 85 sb.append("Saved:").append(intRaw).append("奇数"); 86 } 87 textView1.setText(sb.toString()); 88 89 intRaw++; 90 91 if(intRaw >= 200) 92 { // 100行ずつのファイルを作成 93 mediaScan(); // 完了したファイルをメディアスキャン 94 95 intRaw = 0; // 行数をクリア 96 intFileNumber++; // 次のファイルを作成 97 textView2.setText("Files:" + intFileNumber); 98 } 99// 5/10 handler.postDelayed(this,10); // 10ms間隔で実行 100 handler.postDelayed(this,100); // 100ms間隔で実行 101 } 102 }; 103 }); 104 } 105 106 // メディアスキャン 107 private void mediaScan() 108 { 109 String[] paths = {strFileName1, strFileName2}; 110 String[] mimeTypes = {"text/csv", "text/csv"}; 111 MediaScannerConnection.scanFile(getApplicationContext(), paths, mimeTypes, null); // メディアスキャン 112 } 113 114 115 // ファイルを保存 116 private synchronized void saveFile(final String file, final String str) { 117 new Thread(new Runnable() { 118 @Override 119 public void run() { 120 try { 121 FileOutputStream fos = new FileOutputStream(file,true); // 追記 122 OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8"); 123 BufferedWriter bw = new BufferedWriter(osw); 124// 5/10 for(int i = 0; i < 15000; i++) { // 15000個同じ値を並べる 125 bw.write(str); 126// 5/10 } 127 bw.write("\r\n"); // 改行(Windows用) 128 bw.flush(); 129 bw.close(); 130 Log.i(TAG,"Saved:" + str); 131 132 133 } catch (FileNotFoundException e) { 134 Log.d(TAG, "Error:" + e.toString()); 135 } catch (IOException e) { 136 Log.d(TAG, "Error:" + e.toString()); 137 } 138 } 139 }).start(); 140 } 141} 142

xml

1<?xml version="1.0" encoding="utf-8"?> 2<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:tools="http://schemas.android.com/tools" 4 android:layout_width="match_parent" 5 android:layout_height="match_parent" 6 android:orientation="vertical" 7 android:gravity="center_horizontal" 8 tools:context=".MainActivity"> 9 10 <LinearLayout 11 android:orientation="horizontal" 12 android:gravity="center" 13 android:layout_width="match_parent" 14 android:layout_height="wrap_content"> 15 16 <Button 17 android:id="@+id/button_save" 18 android:text="@string/save_file" 19 android:layout_margin="20dp" 20 android:textSize="20sp" 21 android:layout_width="wrap_content" 22 android:layout_height="wrap_content" /> 23 </LinearLayout> 24 25 <TextView 26 android:id="@+id/text_view1" 27 android:textSize="30sp" 28 android:textColor="#000" 29 android:layout_margin="20sp" 30 android:layout_width="wrap_content" 31 android:layout_height="wrap_content" /> 32 33 <TextView 34 android:id="@+id/text_view2" 35 android:textSize="30sp" 36 android:textColor="#000" 37 android:layout_margin="20sp" 38 android:layout_width="wrap_content" 39 android:layout_height="wrap_content" /> 40</LinearLayout>

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kakajika

2018/05/09 12:10

コードを拝見する限りでは、メモリの解放がされていないわけではなく、10msでは終わらない処理を実行するスレッドを10ms毎にどんどん生成していて、単にメモリを大量に食うプログラムになっているように見えます。これは意図する挙動なのでしょうか?
Wind

2018/05/09 12:18

意図通りファイル保存は失敗すること無く全て書き込まれているので、10msで終わっているのかなと。再度ボタンを押して処理を停止してもNativeメモリは減らないままです。
Wind

2018/05/10 13:59

すいません、「重い処理をやってメモリリークしている」という意図はありません。軽い処理でもNativeが溜まりますので、ソースコードを一部修正します。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問