如何在 Android 中使用内存堆转储数据?
当我们设计一个 Android 应用程序时,开发人员最普遍关心的是程序的内存利用率。这是因为,由于大多数人使用低内存设备,如果您的程序使用大量内存,您可能会失去用户。开发人员正试图定位应用程序中的每一个内存泄漏,以便将其优化为更精简和更快。如果您知道哪些对象在任何时候都在使用内存,那么消除程序中的内存泄漏也更容易。
什么是探查器?
Profiler 是 Android Studio 中的一项功能,可显示有关我们应用程序的 CPU、内存、网络和能源利用率的信息。这是我们应用程序的 CPU、内存、网络和能源利用率的图形表示。我们可以借助这些信息优化我们的代码。但是,在 CPU、内存、网络和能源这四个方面,Memory Profiler 是每个开发人员使用最多的一个。那么,让我们来看看Memory Profiler是什么,然后我们将了解内存泄漏的原因并尝试使用Memory Profiler找到它们。
The Problem Memory Profiler aids in the detection of memory leaks and churn.
当正在运行的应用程序无法访问某个对象但仍继续占用内存时,就会发生内存泄漏。
但是,在Java或 Kotlin 中,我们有一个称为垃圾收集的概念。当您的应用程序的垃圾收集器检测到某些项目不再使用时,它会将未使用的内存返回到堆中。 JVM会首先识别垃圾收集根(GC Root),这是一个可以从堆外访问的对象,例如正在运行的线程或局部变量。然后识别并保存所有可以从垃圾收集根访问的对象。最后,任何无法从垃圾收集根中访问的东西都被视为垃圾并被回收。
当对象尚未销毁但您无法使用它时,就会发生内存泄漏。结果,您的对象将存储内存,您将无法访问它。
In other words, memory leaks occur as a result of abandoned items that will never be used in the program but are still available.
替代方案
我们可以利用内存分析器来发现程序中的内存泄漏。按照以下步骤在我们的应用程序中打开内存分析器:
- 从菜单栏中选择 View > Tool Window > Profiler。
- 在目标设备上,运行您的程序。
- 该图描述了 CPU、内存、网络和能源利用率。可以通过单击内存时间轴上的任意位置来启动 Memory Profiler。将出现以下窗口:
您可以从这里避免两种类型的对象 Activity 和 Fragment 的内存泄漏。这些是令人担忧的问题,因为它们经常消耗大量内存。最好的部分是,从 Android Studio 3.6 或更高版本开始,Memory Profiler 会自动检测活动和片段的内存泄漏。因为这两个类的行为是明确规定的。那么,探查器如何知道这些类是否泄漏?如果 Activity 已终止但仍被引用,则存在泄漏。
如果没有与 Fragment 关联的 FragmentManager 并且仍被引用,则属于泄漏的情况。因此,识别由某些操作或部分引起的泄漏的第一步是记录堆转储。让我们看看如何:
转储内存堆
堆转储用于确定我们应用程序中的哪个对象在进行堆转储时正在消耗内存。堆转储可以通过显示仍在内存中但不会被程序使用的对象来帮助检测内存泄漏。您可以通过捕获堆转储来检索以下信息:
每件事都有它自己的记忆。
- 代码中的每个对象都由一个引用表示。
- 我们的软件分配特定类型的对象。
- 调用堆栈上分配对象的位置。
在 Memory Profiler 中,您可以使用一个名为“Dump Java heap”的选项来捕获堆转储。通过单击它来选择它。
我们有很多信息。我们可以选择四个班级。
它们如下:
- Allocations :此处显示堆中的分配或实例数。
- 此对象类型的本机大小(以字节为单位)表示它使用的本机内存总量(仅对 Android 7.0 或更高版本可见)。
- 浅大小:这是对象消耗的内存量(以字节为单位)。
- 保留大小:这是此类的所有实例使用的内存量(以字节为单位)。
因此,您可以使用此信息来选择值得关注的课程。如果您单击任何类名,Memory Profiler 将打开一个实例视图,这将显示该类的所有实例的列表。可以在此处找到一条新信息,即深度。从任何垃圾收集根到指定实例,深度是最短的跳数。越靠近垃圾回收根的项目,从垃圾回收根到该对象的路径就越多,被垃圾回收的可能性就越小。
如果前面示例中对左侧的任何引用被破坏,第 6 个节点将变得无法访问并被垃圾回收。但是,如果要在绿色节点上进行垃圾收集,则必须从左侧和右侧断开路径。
例子
在使用任何侦听器时,我们必须始终在不使用时注销侦听器,因为如果不这样做,侦听器将保留在 GC Root 中并且永远不会被垃圾回收。因此,尽可能尝试在 onPause()、onStop() 或 onDestroy() 方法中取消注册侦听器。 LocationListener 的示例如下:
Java
public class GeeksforGeeksAndroid
extends Activity implements doCourse {
@Override
public void onCourseAdded(Location course)
{
// Do something
}
@Override public void onStart()
{
LocationListener.get().register(this);
}
@Override public void onStop()
{
LocationListener.get().unregister(this);
}
}
结论
如果您不取消注册侦听器,您将在转储Java堆中获得状态,表明 LocationListener 导致内存泄漏。这样您就可以使用 Memory Profiler 堆转储中的数据发现泄漏,然后优化和修复代码以获得更好的结果。