Androidオペレーティングシステムのメモリ管理に関するこの質問については非常に興味があるので、それに関する非常に詳細な回答を期待していますトピック。
私が知りたいこと:
最も重要な:
これまでに聞いたこと(2013年まで):
私を非常に好奇心をそそるもの:
これらの制限は両方とも非常に低いです。
最近、デバイスのRAMを確認するために Android Task Manager をダウンロードしました。私が気づいたのは、約40〜50メガバイトのRAMを使用しているアプリケーションがあることです。これは、前述の最大32 MBのRAM使用量よりも多くなります。では、Androidはアプリが使用できるRAMの量をどのように決定しますか?アプリがその制限を超える可能性はありますか?
さらに、約30〜40メガバイトを使用していると、一部のアプリが(=システムによって殺されて)クラッシュOutOfMemoryExceptionされることに気付きました。一方、100 MB以上を使用してスマートフォンで実行しているアプリは、しばらくして(おそらくメモリリークが原因で)クラッシュしたり、殺されるそのため、明らかにRAMをどれだけ節約できるかを判断する際には、アプリ自体にも依存します。これはどのように可能ですか? (768 MB RAMのHTC One Sでテストを実施しました)
免責事項:私はAndroid Task Managerアプリとは一切関係ありません。
Androidアプリケーション(システムアプリではない)が使用できるメモリの最大量(メガバイト単位/ RAM全体のパーセンテージ)はいくらですか?
それはデバイスによって異なります。 getMemoryClass()
ON ActivityManager
は、コードが実行されているデバイスの値を提供します。
Androidバージョン間に違いはありますか?
はい、OS要件が長年にわたって増加しており、デバイスがそれに合わせて調整する必要がある場合に限ります。
デバイスのメーカーに関して違いはありますか?
はい、メーカーがデバイスを製造する限り、サイズはデバイスによって異なります。
アプリが使用できるRAM量を決定する際に考慮される「サイドファクター」はどれですか?
「副次的要因」の意味がわかりません。
初期のデバイスのアプリごとの上限は16MBでした。その後のデバイスでは、24MBまたは32MBに増加しました
そうです。画面解像度は重要な決定要因です。解像度が大きいほどビットマップが大きくなるため、タブレットや高解像度の携帯電話はまだ高い値を持つ傾向があります。たとえば、48MBのヒープを持つデバイスが表示されますが、それ以上の値があったとしても驚かないでしょう。
アプリがその制限を超える可能性はありますか?
あなたは、そのアプリの作者が自分が何をしているか知っていると仮定します。 アプリのメモリ使用量はコアAndroidエンジニアが判断するのが難しい を考慮すると、問題のアプリが必ずしも特に正確な結果を提供するとは思わないでしょう。
つまり、ネイティブコード(NDK)はヒープ制限の対象ではありません。また、Android 3.0以降、アプリは通常、数百MBの範囲の「大きなヒープ」を要求できますが、これはほとんどのアプリでは不十分な形式と見なされます。
さらに、約30〜40メガバイトを使用すると、私のアプリの一部がOutOfMemoryExceptionでクラッシュすることに気付きました。
Androidガベージコレクタは圧縮ガベージコレクタではないことに注意してください。例外は実際にはCouldNotFindSufficientlyLargeBlockOfMemoryException
である必要がありますが、それはおそらく冗長すぎると思われました。 OutOfMemoryException
は、要求されたブロックを割り当てられなかったことを意味し、ヒープを完全に使い果たしたことを意味しません。
2018年の終わりなので、状況は変わりました。
まず、アプリを実行し、Android StudioでAndroid Profilerタブを開きます。どれだけのメモリを消費するかがわかりますが、驚くでしょうが、大量のRAMを割り当てることができます。
また、 これは素晴らしい記事です 公式ドキュメントのMemory Profilerの使用方法に関する詳細な説明があり、メモリ管理の詳細を確認できます。
ただし、ほとんどの場合、通常のAndroidプロファイラーで十分です。
通常、アプリは50MbのRAM割り当てで開始されますが、メモリに写真の読み込みを開始するとすぐに90Mbにジャンプします。プリロードされた写真(各3,5Mb)を含むViewPagerでアクティビティを開くと、190Mbを数秒で簡単に取得できます。
しかし、これはメモリ管理に問題があるという意味ではありません。
私ができる最善のアドバイスは、ガイドラインとベストプラクティスに従って、画像の読み込みにトップライブラリ(Glide、Picasso)を使用することです。
しかし、何かを調整する必要があり、手動で割り当てることができるメモリの量を本当に知る必要がある場合は、合計空きメモリを取得し、そこから所定の部分(%)を計算できます。私の場合、ユーザーがリストをスライドするたびに復号化する必要がないように、復号化された写真をメモリにキャッシュする必要がありました。
このために、すぐに使用できる LruCacheクラス を使用できます。これは、オブジェクトが割り当てるメモリの量(またはインスタンスの数)を自動的に追跡し、使用履歴によって最も古いものを削除するために最も古いものを削除するキャッシュクラスです。 こちらです 使い方の素晴らしいチュートリアルです。
私の場合、キャッシュの2つのインスタンスを作成しました:サムとアタッチメント用です。シングルトンアクセスで静的にし、アプリ全体でグローバルに利用できるようにしました。
キャッシュクラス:
public class BitmapLruCache extends LruCache<Uri, byte[]> {
private static final float CACHE_PART_FOR_THUMBS_PRC = 0.01f; // 1% (Nexus 5X - 5Mb)
private static final float CACHE_PART_FOR_ATTACHMENTS_PRC = 0.03f;// 3% (Nexus 5X - 16Mb)
private static BitmapLruCache thumbCacheInstance;
private static BitmapLruCache attachmentCacheInstance;
public static synchronized BitmapLruCache getDecryptedThumbCacheInstance() {
if (thumbCacheInstance == null) {
int cacheSize = getCacheSize(CACHE_PART_FOR_THUMBS_PRC);
//L.log("creating BitmapLruCache for Thumb with size: " + cacheSize + " bytes");
thumbCacheInstance = new BitmapLruCache(cacheSize);
return thumbCacheInstance;
} else {
return thumbCacheInstance;
}
}
public static synchronized BitmapLruCache getDecryptedAttachmentCacheInstance() {
if (attachmentCacheInstance == null) {
int cacheSize = getCacheSize(CACHE_PART_FOR_ATTACHMENTS_PRC);
// L.log("creating BitmapLruCache for Attachment with size: " + cacheSize + " bytes");
attachmentCacheInstance = new BitmapLruCache(cacheSize);
return attachmentCacheInstance;
} else {
return attachmentCacheInstance;
}
}
private BitmapLruCache(int maxSize) {
super(maxSize);
}
public void addBitmap(Uri uri, byte[] bitmapBytes) {
if (get(uri) == null && bitmapBytes != null)
put(uri, bitmapBytes);
}
public byte[] getBitmap(Uri uri) {
return get(uri);
}
@Override
protected int sizeOf(Uri uri, byte[] bitmapBytes) {
// The cache size will be measured in bytes rather than number of items.
return bitmapBytes.length;
}
}
これは、利用可能な無料のRAMを計算する方法と、それをどれだけ噛むことができるかです。
private static int getCacheSize(float partOfTotalFreeMemoryToUseAsCache){
final long maxMemory = Runtime.getRuntime().maxMemory();
//Use ... of available memory for List Notes thumb cache
return (int) (maxMemory * partOfTotalFreeMemoryToUseAsCache);
}
そして、これは私がアダプタでそれを使用してキャッシュされた画像を取得する方法です:
byte[] decryptedThumbnail = BitmapLruCache.getDecryptedThumbCacheInstance().getBitmap(thumbUri);
バックグラウンドスレッドでキャッシュに設定する方法(通常のAsyncTask):
BitmapLruCache.getDecryptedThumbCacheInstance().addBitmap(thumbUri, thumbBytes);
私のアプリはAPI 19+を対象としているため、デバイスは古くなく、利用可能なRAMのこれらの部分は、私のケースではキャッシュに十分です(1%および3%)。
面白い事実:Androidには取得するAPIやその他のハックはありませんアプリに割り当てられるメモリの量は、さまざまな要因に基づいてオンザフライで計算されます。
追伸キャッシュを保持するために静的クラスフィールドを使用していますが、最新のAndroidガイドラインに従って、そのために ViewModelアーキテクチャコンポーネント を使用することをお勧めします。
アプリごとのメモリ制限は、画面サイズとAndroidバージョンによって異なります: https://drive.google.com/file/d/0B7Vx1OvzrLa3Y0R0X1BZbUpicGc/view?usp=sharing
ソース:Android互換性ダウンロード http://source.Android.com/compatibility/downloads.html ;互換性定義ドキュメント(CDD)、セクション仮想マシンの互換性またはランタイムの互換性