誰か教えてくださいorz

アバター
Dixq (管理人)
管理人
記事: 1662
登録日時: 14年前
住所: 北海道札幌市
連絡を取る:

誰か教えてくださいorz

投稿記事 by Dixq (管理人) » 12年前

管理人が掲示板で質問するわけにいかないので、ここでひっそり質問を・・・。

Androidの1つのアプリが使用可能なメモリ量っていくらなんでしょう。

私の理解ではメモリに「Dalvikヒープ」と「Nativeヒープ」が存在し(合わせたヒープを仮にアプリヒープと呼ぶ)、その合計(アプリヒープ)が一定の量を超えてはならないと思っています。
つまりあるメモリ量をDalvikヒープとNativeヒープが分け合っている状態です。

Android2.3系で、Dalvikヒープをある程度使用した状態で、ByteBuffer.allocateDirect()を繰り返してNativeヒープを消費し続けたところ

画像(←ここで落ちる)

確かにDalvikヒープとNativeヒープの合計がmaxを超えるところでOutOfMemoryが発生します。
maxと言っても、ここで表示しているmaxは
Runtime.getRuntime().maxMemory()
の値です。
つまりDalvikヒープの最大量だと思われます。。。。じゃアプリ最大ヒープ量はいくらなの・・? =Dalvik最大ヒープ量?

もう一つ疑問があります。
Nativeを今度はallocateDirectではなく、実際にndkでCを使ってmallocし続けたところ

画像

今度はmaxを超えて確保し続けても落ちることがありません。恐らくLinuxヒープを使っているのかいくらでも確保できます。
じゃここに表示されているNativeヒープ(Debug.getNativeHeapSize())は何?

結局1つのアプリでいくらメモリが使えるのかよく分からんとです・・・。

テストに使ったアプリのソースコードをここにアップします。
http://dixq.net/blog/13.07.25/memorytest.zip
最後に編集したユーザー Dixq (管理人) on 2013年7月25日(木) 21:14 [ 編集 2 回目 ]

ISLe
記事: 2650
登録日時: 14年前

Re: 誰か教えてくださいorz

投稿記事 by ISLe » 12年前

リファレンスによるとByteBuffer#allocateはバッファをバイト配列として確保、ByteBuffer#allocateDirectはメモリブロックとして確保するとあります。
Javaの配列はオブジェクトなので前者は配列のオーバーヘッドを含めた実装になっていると思われます。
後者はC/C++のように連続したメモリブロックを一括で確保してアクセスする低レベルな実装になっていると思われます。

ByteBuffer.allocateDirectは実装上の都合でNativeヒープからメモリブロックを確保しているのではないかと思います。
しかしVM上のプロセスとしては全体で最大消費可能メモリを超えないように実装されているのではないでしょうか。

NDKはVM上のプロセスとしてではなく、VM自身のプロセスから制限なしにヒープを消費していくことになると思います。
APIからNativeヒープサイズとして取得できる値を超えて確保すると、VM自体の動作に支障をきたす恐れがあるのではないでしょうか。

OutOfMemory例外が発生しない範囲で確保するのが無難ではないかと思います。
最後に編集したユーザー ISLe on 2013年7月26日(金) 00:43 [ 編集 2 回目 ]

アバター
Dixq (管理人)
管理人
記事: 1662
登録日時: 14年前
住所: 北海道札幌市
連絡を取る:

Re: 誰か教えてくださいorz

投稿記事 by Dixq (管理人) » 12年前

> ISLeさん

そうなのでしょうね~。
しかし「アプリが使えるメモリは後いくらなのか」という情報はどうしたら取得できるんでしょう・・。
今私がしたいのは、メモリが少なくなってきたら、蓄えているバッファを少しずつ解放するということです。
同じNativeヒープでも中身が異なるのであれば計算のしようが無いですね・・・。

ISLe
記事: 2650
登録日時: 14年前

Re: 誰か教えてくださいorz

投稿記事 by ISLe » 12年前

アプリが使えるメモリというのは、
Dixq (管理人) さんが書きました:確かにDalvikヒープとNativeヒープの合計がmaxを超えるところでOutOfMemoryが発生します。
というところから逆算できるのではないでしょうか。

それ以上のメモリ確保は、優先度の低いアプリを強制終了するとかメモリ掃除とかを目的とするようなシステムレベルの話になるでしょう。
OSやVMの動作を把握しないと安定動作は難しいかと。
ソースコードは公開されているのでできないことはないですけどバージョンアップ対応とか面倒なことがたくさん付いてくると思います。

アバター
Dixq (管理人)
管理人
記事: 1662
登録日時: 14年前
住所: 北海道札幌市
連絡を取る:

Re: 誰か教えてくださいorz

投稿記事 by Dixq (管理人) » 12年前

上に書いている通り、
Javaから取ったNativeヒープ①と、NDKから取ったNativeヒープ②では同じNativeヒープでも訳が違うようで、
DalvikヒープとNativeヒープの合計が①の場合落ち、②の場合落ちません。
ですから①と②が混在している時はいつ落ちるか予測が付きません・・ということで困っています。

最近の端末は非常にメモリが潤沢ですから、32MBのような壁は越えて使いたいものです。

ISLe
記事: 2650
登録日時: 14年前

Re: 誰か教えてくださいorz

投稿記事 by ISLe » 12年前

ByteBuffer#allocateDirectはNativeヒープを確保するもVMを通してますから、VMがランタイムメモリの消費量として加算していて例外が発生するのでしょう。
ネイティブコードはVM通さないので例外が発生しない、それだけの違いかと思います。

でネイティブコードでどれだけのメモリを確保可能かという話であればシステムコールを使えば良いのではないでしょうか。
と思って調べてみたのですがAndroidはどうなのかわかりませんが、Linuxは楽観的メモリ配置戦略を用いていて確保するだけならいくらでも確保できるそうです。
アクセスしてみてメモリ不足なら落ちるという具合。
物理メモリの使用状況は/proc/meminfoにアクセスして読み取るしかないみたいですが、こちらもAndroidで通用するか不明。

アバター
Dixq (管理人)
管理人
記事: 1662
登録日時: 14年前
住所: 北海道札幌市
連絡を取る:

Re: 誰か教えてくださいorz

投稿記事 by Dixq (管理人) » 12年前

私が知りたいのは要するに以下のようなものです。

今の瞬間Nativeヒープの使用量が32MBである。
この時、VMを通した消費メモリとVMを通していない消費メモリサイズはそれぞれいくらなのか?

こんな感じです。
そうすれば後いくら使えるかが明確にわかりそうです。