ガベージコレクタは、それが必要として、Androidのアプリケーションで「メモリスラッシュ」解放ません

Talendar:

こんにちは!

私は、初心者のJavaとAndroidの開発者だと私は私のアプリのメモリ管理を扱う最近問題を抱えてきました。私はそれがより明確にし、読みやすくするために、セクションにこのテキストを中断します。


私のアプリの簡単な説明

これは、いくつかの段階(レベル)で構成されたゲームです。各ステージは、次のステージにプレイヤーを導く、プレーヤ及び出口のための出発点を有しています。各ステージには、障害物の独自のセットを持っています。プレイヤーが最終段階に達したときに、現在、(私は作成した4は、これまでのところ)彼/彼女は、自動的に第一段階(レベル1)に戻ります。

呼ばれる抽象クラスゲームオブジェクトは、(延びAndroid.Viewを)プレーヤ、ゲーム内の他のすべてのオブジェクト(障害物、等)が存在するための基本構造と動作を定義します。(基本的に、ある、ビュー)のすべてのオブジェクトは(でframeLayoutを拡張)私が作成したカスタムビューに描かれています。ゲームロジック、ゲームループは側スレッド(gameThread)によって処理されます。ステージはXMLファイルからメタデータを取得することによって作成されます。

問題

(私が見つけ、解決に取り組んできたすべてが)私のコード上のすべての可能なメモリリークのほかに、ガベージコレクタの出来事に関連する奇妙な現象があります。代わりに、あなたは混乱の言葉とリスクとそれを記述する、私はイメージを使用します。孔子が言ったように、「画像は千の言葉の価値があります」。私のGIFは、以下の150個のフレームを持っているので、まあ、この場合には、私はちょうど15万個の言葉を読んでからあなたを保存しました。

最初にロードされたステージ1。 アプリケーションの総メモリ割り当ては85メガバイトです。 二度目のロードステージ1。 アプリケーションの総メモリ割り当ては130メガバイトです。 私は強制的に(Androidのプロファイラを使用して)2つのガベージコレクションを実行した後、メモリは85メガバイトに戻ります。

Description: the first image represents my app's memory usage when the "stage 1" is first loaded. The second image (GIF) firstly represents my app's memory usage timeline when the "stage 1" is loaded for the second time (this happens, as described earlier, when the player beat the last stage) and is followed by four garbage collections forcefully initiated by me.

As you might have noticed, there is a huge difference (almost 50MB) in the memory usage between the two situations. When the "Stage 1" is firstly loaded, when the game starts, the app is using 85MB of memory. When the same stage is loaded for the second time, a little bit later, the memory usage is already at 130MB! That's probably due to some bad coding on my part and I'm not here because of this. Have you noticed how, after I forcefully performed 2 (actually 4, but only the first 2 mattered) garbage collections, the memory usage went back to it's "normal state" (the same memory usage as when the stage was firstly loaded)? That's the weird phenomenon I was talking about.

The question

If the garbage collector is supposed to remove from memory objects that are no long being referenced (or, at least, have only weak references), why is the "trash memory" that you saw above being removed only when I forcefully call the GC and not on the GC's normal executions? I mean, if the garbage collection manually initiated by me could remove this "thrash", then the normal GC's executions would be able to remove it as well. Why isn't it happening?

I've even tried to call System.gc() when the stages are being switched, but, even though the garbage collection happens, this "thrash" memory isn't removed like when I manually perform the GC. Am I missing something important about how the garbage collector works or about how Android implements it?

Final considerations

I've spent days searching, studying and making modifications on my code but I could not find out why this is happening. StackOverflow is my last resort. Thank you!

NOTE: I was going to post some possibly relevant part of my app's source code, but since the question is already too long I will stop here. If you feel the need to check some of the code, just let me know and I will edit this question.

What I have already read:
How to force garbage collection in Java?
Garbage collector in Android
Java Garbage Collection Basics by Oracle
Android Memory Overview
Memory Leak Patterns in Android
Avoiding Memory Leaks in Android
Manage your app's memory
What you need to know about Android app memory leaks
View the Java heap and memory allocations with Memory Profiler
LeakCanary (memory leak detection library for Android and Java)
Android Memory Leak and Garbage Collection
Generic Android Garbage Collection
How to clear dynamically created view from memory?
How References Work in Android and Java
Java Garbage Collector - Not running normally at regular intervals
Garbage Collection in android (Done manually)
... and more I couldn't find again.

Stephen C :

Garbage collection is complicated, and different platforms implement it differently. Indeed, different versions of the same platform implement garbage collection differently. (And more ... )

A typical modern collector is based on the observation that most objects die young; i.e. they become unreachable soon after they are created. The heap is then divided into two or more "spaces"; e.g. a "young" space and an "old" space.

  • The "young" space is where new objects are created, and it is collected frequently. The "young" space tends to be smaller, and a "young" collection happens quickly.
  • The "old" space is where long-lived objects end up, and it is collected infrequently. On "old" space collection tends to be more expensive. (For various reasons.)
  • Object that survive a number of GC cycles in the "new" space get "tenured"; i.e they are moved to the "old" space.
  • Occasionally we may find that we need to collect the new and old spaces at the same time. This is called a full collection. A full GC is the most expensive, and typically "stops the world" for a relatively long time.

(There are all sorts of other clever and complex things ... which I won't go into.)


Your question is why doesn't the space usage drop significantly until you call System.gc().

The answer is basically that this is the efficient way to do things.

The real goal of collection is not to free as much memory all of the time. Rather, the goal is to ensure that there is enough free memory when it is needed, and to do this either with minimum CPU overheads or a minimum of GC pauses.

So in normal operation, the GC will behave as above: do frequent "new" space collections and less frequent "old" space collections. And the collections will run "as required".

But when you call System.gc() the JVM will typically try to get back as much memory as possible. That means it does a "full gc".

Now I think you said it takes a couple of System.gc() calls to make a real difference, that could be related to use of finalize methods or Reference objects or similar. It turns out that finalizable objects and Reference are processed after the main GC has finished by a background thread. The objects are only actually in a state where they can be collected and deleted after that. So another GC is needed to finally get rid of them.

Finally, there is the issue of the overall heap size. Most VMs request memory from the host operating system when the heap is too small, but are reluctant to give it back. The reason is that collectors are generally most efficient if the proportion of garbage to non-garbage is high. By giving emptied memory back to the OS, a JVM is going to impact performance latter ... if the heap grows again.

I'm not sure about the Android collector, but the Oracle collectors note the free space ratio at the end of successive "full" collections. They only reduce the overall size of the heap if the free space ratio is "too high" after a given number of cycles.

Assuming that the Android collector works the same way, that is another explanation for why you had to run System.gc() multiple times to get the heap size to shrink.

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=204378&siteId=1