为什么在Android Studio的Profiler(内存)中,FinalizerReference类的保留堆大小如此之大? [英] Why is the retained heap size of the FinalizerReference class so large in the (memory) Profiler of Android Studio?

查看:299
本文介绍了为什么在Android Studio的Profiler(内存)中,FinalizerReference类的保留堆大小如此之大?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已阅读此问题关于Finalizer在堆中的最大份额.它始于2011年,当时工具有所不同,而Java类仍具有不同的名称(Finalizer与FinalizerReference).所以我认为现在可以问这个类似但又新的问题.

I have read this question about Finalizer's lion share of the heap. It dates from 2011 when the tools were different and the Java class still had a different name (Finalizer vs FinalizerReference). So I think this similar but new question can be asked now.

最重要的是,该问题的公认答案归结为:避免使用finalize()对象.直接或间接使用finalize()的Android类包括Canvas,Paint,Bitmap,Drawable和RenderNode.祝你好运,一直都在避免.

On top of that, the accepted answer to that question boils down to: avoid using finalize()'d objects. Android classes that use finalize() directly or indirectly include Canvas, Paint, Bitmap, Drawable and RenderNode. Good luck avoiding all of them all of the time.

我还阅读了 Profiler文档,并通过了

I have also read the Profiler documentation and worked through the Memory Profiler Codelab.

后者将保留大小"定义为此类所有实例都在控制的内存大小."

The latter defines "Retained Size" as "Size of memory that all instances of this class are dominating."

这就是问题所在:我在 Codelabs内存重载应用程序(顺便说一句,它是被设计破坏的).我将添加的TextView的数量限制为2000,并且仅轻按了设备上的浮动操作按钮一次.转储堆时,Profiler报告FinalizerReference的保留大小为测试设备上可用内存的两倍.显然,某些占主导地位的内存不只一次.

So here is the problem: I ran Profiler on the Codelabs Memory Overload app (which is broken by design, by the way). I limited the number of added TextViews to 2000, and tapped the floating action button on the device only once. On dumping the heap, Profiler reported the Retained Size of FinalizerReference to be double the memory available on my test device. Obviously some dominated memory is counted more than once.

当然,我对自己使用程序的堆使用确实很感兴趣.当探查器在堆的顶部显示FinalizerReference时,它似乎具有误导性,它控制着每个可用字节以及更多字节.我应该忽略FinalizerReference保留的堆大小吗?为什么我应该相信为其他班级提供的价值?

Of course I am really interested in the heap use of my own programs. Profiler seems to be misleading when it shows FinalizerReference at the top of the heap, dominating every byte available and more. Should I ignore the retained heap size of FinalizerReference? Why should I trust the value given for other classes?

推荐答案

探查器似乎可以像其他任何类一样计算FinalizerReference的保留堆大小.这是一个错误,因为FinalizerReference在垃圾回收方面具有独特的自反性(详见下文).

Profiler seems to calculate the Retained heap size for FinalizerReference like that of any other class. This is a mistake, as FinalizerReference is unique in its reflexivity with respect to garbage collection (more below).

一个简单的示例将说明这导致的荒谬结果.这也将弄清楚为什么FinalizerReference似乎可以支配比系统中可用内存更多的内存.

A simple example will demonstrate the absurd result to which this leads. It will also make clear why FinalizerReference can seem to dominate more memory than is available in the system.

在Android Studio中,对 Codelabs内存过载并转储堆. (您可能需要触发垃圾回收并在转储之前等待其完成,以获取下面的结果.)切换到zygote堆.在堆转储"窗格上(可能在顶部)找到FinalizerReference.找到分配列中列出的实例数,例如n.计算n *(n + 1)/2 * 36.它等于保留大小"列下的数字吗?我是这样想的.

In Android Studio, Profile an app like Codelabs Memory Overload and dump the heap. (You may need to trigger garbage collection and wait for its completion before dumping, to get the result below.) Switch to the zygote heap. Find FinalizerReference on the Heap Dump pane, likely at the top. Find the number of instances listed in the column Allocations, say n. Calculate n*(n+1)/2*36. Does it equal the number under the column Retained Size? I thought so.

为什么此公式有效?你自己看.在堆转储"窗格上选择"FinalizerReference".在实例视图"中向下滚动实例列表.根据需要经常选择单击以查看下一个100",以到达列表的底部.选择最后一个实例.请注意,在下面的窗格中,其他某些引用它的FinalizerReference内有一个下一个"字段,但没有上一个"字段.还要注意,在这种情况下,浅"和保留"大小是相同的,即36个字节.然后查看保留大小"的顺序,该顺序在列表中:36、72、108、144,...现在将所有n个实例的值相加.

Why does this formula work? See for yourself. Select FinalizerReference on the Heap Dump pane. Scroll down the list of instances in the Instance View. Select "Click to see next 100" as often as necessary to get to the bottom of the list. Select the last instance. Note on the pane below that there is a "next" field inside some other FinalizerReference that references it, but no "prev" field. Also observe that for this instance, the Shallow and Retained sizes are the same, namely 36 bytes. Then look at the sequence of Retained sizes going up the list: 36, 72, 108, 144, ... Now add up those values for all the n instances.

上面给出的公式不适用于应用程序堆,这有两个原因.一种是堆有时包含FinalizerReference实例,这些实例已从链接列表中取出,但尚未被垃圾回收.可以通过查看其内容(显示空引用)来识别它们,next和prev也为空.另一个是 app 实例列表底部的项目由 zygote 实例列表顶部的项目引用.因此,只能通过考虑合子堆上的实例并排除所有未链接的实例,才能计算出应用程序堆上FinalizerReference的(声称的)保留大小.

The formula given above does not (simply) work for the app heap, for two reasons. One is that the heap sometimes contains instances of FinalizerReference that have been taken out of the linked list, but have not yet been garbage collected. They can be identified by looking at their contents, which shows a null referent, with next and prev also null. The other is that the item at the bottom of the app instance list is referred to by the item at the top of the zygote instance list. So the (purported) Retained size of FinalizerReference on the app heap can only be calculated by taking into account the instances on the zygote heap as well, and by excluding all unlinked instances.

这是东西. FinalizerReference不是普通的类.它是垃圾回收期间垃圾回收器使用的类.这种反射性很重要. FinalizerReference实例的垃圾收集仅由垃圾收集触发.

Here is the thing. FinalizerReference is not an ordinary class. It is a class used by the garbage collector, during garbage collection. This reflexivity is important. The garbage collection of FinalizerReference instances is only triggered by garbage collection.

在创建时,FinalizerReference实例将成为双向链接列表的一部分,因此可以删除任何位置的实例而不会破坏列表.但这也意味着大多数实例都引用了另外两个实例.但是,唯一可以删除这些引用的操作是垃圾回收.垃圾收集器会找到终结器实例中没有被引用的每个对象,运行其finalize()方法,进行垃圾收集,然后从列表中删除引用该对象的FinalizerReference实例,从而允许该实例依次收集垃圾.

On creation, FinalizerReference instances are made part of a doubly linked list, so that an instance in any position can be removed without breaking the list. But this also means that most instances maintain references to two others. However, the only operation that can remove those references is a garbage collection. The garbage collector finds every object that is referenced by nothing but a FinalizerReference instance, runs its finalize() method, garbage collects it and removes the FinalizerReference instance that refers to it from the list, allowing that instance to be garbage collected in turn.

此刻Profiler所做的是将FinalizerReference的第一个"实例计算为具有36字节的保留大小,等于其浅"大小.对于第二个实例,它计算自己的36个Shallow字节,再加上第一个实例的36字节的Retained大小,并对其进行引用.对于第三个实例,它将计算自己的36个Shallow字节,再加上前两个实例的72 + 36 Retained大小.因此,当我们达到100时,第一个实例的内存已被计数100次,第二个实例的内存已被计数99次,依此类推. )内存控制"的递归定义.

What Profiler does at the moment, is to count the "first" instance of FinalizerReference as having a Retained size of 36 bytes, equal to its Shallow size. For the second instance, it counts its own 36 Shallow bytes, plus the 36 byte Retained size of the first instance, to which it has a reference. For the third instance, it counts its own 36 Shallow bytes, plus the 72 + 36 Retained sizes of the previous two instances. So when we get to number 100, the memory of the first instance has been counted 100 times, that of the second instance 99 times, etc. This makes no sense, apart perhaps from a (misleading and meaningless, in the case of this class) recursive definition of "memory domination".

对于开发人员而言,有关FinalizerReference实例的有趣事情不是它引用的其自己类的其他实例,而是它的引用对象,尤其是在该引用对象没有其他引用的情况下.如果Profiler对于该类有用,它将计算FinalizerReference类的保留大小,作为由仅FinalizerReferences实例引用的 referents 占用的内存之和.那将永远不会超过系统中的实际内存,并且任何过高的值都会通知开发人员一个问题,即对象的创建和丢弃速度超过了垃圾回收的速度.

For the developer, the interesting thing about a FinalizerReference instance is not the other instances of its own class that it refers to, but its referent, especially if that referent has no other references to it. If Profiler would be useful for this class, it would calculate the Retained size of the FinalizerReference class as the sum of the memory occupied by the referents referred to only by FinalizerReferences instances. That would never be more than the actual memory in the system, and any inordinate value would inform the developer of a problem where objects are created and discarded faster than they can be garbage collected.

就目前情况而言,Profiler仅确认用于求和连续整数的数学公式(似乎遍历FinalizerReference列表,并实际上将这些数字相加!).从这个意义上说,这没有错,但是将结果解释为FinalizerReference的保留堆大小仍然是一个错误.这是一种误导,当然也无助于开发人员了解堆中正在发生的事情.

As things stand, Profiler only confirms the mathematical formula for summing consecutive integers (by traversing the FinalizerReference list, it seems, and actually adding up those numbers!). In this sense it is not wrong, but its interpretation of the result as the retained heap size of FinalizerReference is still an error. It is misleading, and it certainly does not help the developer to understand what is happening on the heap.

这篇关于为什么在Android Studio的Profiler(内存)中,FinalizerReference类的保留堆大小如此之大?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆