Google IO:Android内存管理主题演讲记录

来源:互联网 发布:歌曲剪辑拼接软件 编辑:程序博客网 时间:2024/05/11 03:25

全程谷歌娘翻译的:  个人觉得谷歌娘已经很强大了。

大家好,


今天我的名字叫帕特里克Dubroy,我要和你谈谈内存管理为Android。所以,我真的很高兴在这里看到这么多的人谁关心内存管理,尤其是在一天快要结束。


所以,让我们开始吧。所以我敢肯定,你都记得这个装置。这是T-Mobile的G1。雨果在昨天的主题演讲谈论它。它大约两年半前发布。那么,有没有人在这里究竟是谁在G1上开发出来的?好吧,令人印象深刻。这就是房间的约也许40%。所以,你可能记得G1附带的RAM 192兆字节。而事实上,大部分的,是由该系统所使用的。有没有遗留的应用程序一大堆。


一晃几年过去了,我们对摩托罗拉Xoom刚刚发布了几个月前。 Xoom的自带内存技嘉。现在,有些人可能会听到这样想,OK,我的麻烦已经结束了。我再也不用担心内存管理。当然,因为我们有整个房间在这里,你们都是聪明人,你知道,这不是真的。这里面有几个原因。首先,Xoom的有六个半倍的分辨率的G1了。所以,你有六年半倍,在屏幕上的像素。这意味着你可能会需要使用更多的内存。你有例如更多的位图。另一件事是,在平板电脑上,你真的想创造一种新的应用程序。你知道的,丰富的,身临其境的应用,这样的YouTube应用程序,例如。这些都是要占用大量的内存。有位图吨在这里。那些占用大量的内存。此外,Xoom的,我们正在谈论的是一个漂亮的新设备。这基本上是最前沿的硬件。大多数客户都不会使用的东西,只有两个月大。所以,当然,你要支持谁使用旧的硬件以及人。


最后,也许你都熟悉帕金森定律,它说你有工作,始终把尽可能多的时间。真的,这是一种相同的软件。所以,不管你有多少内存,你会找到一个方法来使用它,希望你有更多的只是一点点。


我想谈谈今天基本上是两回事。首先,我想介绍一些我们在姜饼已经取得了会影响您的应用程序是如何使用内存的变化和蜂窝。这就是你的客串演出。好吧,所以我刚才说,有两种不同的事情,我今天想掩盖。所以首先,我想谈一些我们在姜饼做会影响您的应用程序如何使用内存的变化和蜂窝。基本上,内存管理一般。在谈话的下半年我想谈谈一些工具,你可以用它来更好地了解您的应用程序如何使用内存。如果你有内存泄漏,如何能够找出其中的内存泄漏。


因此,只要设置这个谈话的期望,我将让他们对你所做的东西一些假设,它会真正帮助您得到最出这一点,如果你熟悉这些概念。所以,我希望你们都写之前的Android应用程序。它看起来像约一半的房间都在G1上开发的,所以这可能是真实的。我希望你们大多数人都听过的Dalvik堆。你有什么我谈论当我谈到堆内存的基本理念。我敢肯定,你所熟悉的垃圾收集器。你有一个基本的想法希望什么垃圾收集的是,它是如何工作的。大概,大部分你已经看到之前OutOfMememoryError,你有为什么你得到它,你可以做什么来对付它的基本思想。


因此,首先,让我们来谈谈堆大小。所以,你可能知道,在Android中,有您的应用程序的堆大小的硬性限制。还有的这几个原因。所以首先,安卓的一大特色是,它有完整的多任务处理。所以,你实际上有同时运行多个程序。所以很明显,每一个不能使用所有的设备内存。我们也不想失控应用刚开始越来越大,并腹胀整个系统。你总是希望你的拨号器开始工作,你的发射器的工作,诸如此类的事情。所以这是在堆大小此硬性限制,如果你的应用程序需要分配更多的内存,你已经走了到那个堆大小限制已经,那么你基本上会得到一个内存不足的错误。因此,这堆大小限制是设备相关。它改变了很多在过去几年。在G1是16兆字节。在Xoom的它现在是48兆字节。所以这是一个有点大。


如果你正在写一个应用程序,你想知道的,OK,多少堆空间我有可用?你知道,也许你要决定多少东西保持例如缓存。有您可以在ActivityManager,getMemoryClass使用一种方法,将返回以MB为单位的整数值,这是你堆的大小。现在,这些限制被设计假设你知道差不多,你想建立在Android上应该能够适合在这些限制任何应用程序。


当然,也有一些应用程序,是真正占用大量内存。正如我所说,在平板电脑上,我们真的要建立几乎一类新的应用程序。它比那种你建立在手机的事情完全不同。所以我们认为,如果有人想建立一个图像编辑器,例如,对Xoom的,他们应该能够做到这一点。但是,一个图像编辑器是一个非常内存密集型应用程序。这是不可能的,你可以建立一个良好的一个使用的堆不到48兆字节。因此,在蜂窝我们增加了一个新的选项,允许这样的应用程序,以获得更大的堆大小。基本上,你可以把东西在你的AndroidManifest,largeHeap等于true。这将允许应用程序使用更多的堆。同样,还有你可以用它来确定您有多少内存提供给您的方法。该ActivityManager getLargeMemoryClass方法再次,将返回这个大堆大小的整数值。


现在,我们再往前走之前,我想在这里带来了很大的警告。要知道,这是不是你应该仅仅因为你有一个内存不足的错误做,或者你认为你的应用程序更值得如此深厚堆。你不打算做自己任何好处,因为你的应用是怎么回事,因为更大的堆意味着你要在垃圾收集花费更多的时间更多的表现不佳。此外,您的用户可能将注意到,因为他们所有的其他应用也越来越踢出内存。这真的要保留,当你真正了解好了,我用吨内存的东西,我知道为什么我使用的内存,我真的需要使用该内存。这是你应该使用这个大堆选项中唯一一次。所以我提到的垃圾收集。而这需要更长的时间,当你有一个更大的堆。让我们来谈谈垃圾收集一点点。所以,我只是想通过一个快速的解释,这里的什么垃圾收集在做什么。所以基本上,你有一组对象。首先,让我们在这里说,这些蓝色的物体,这些都是在你堆中的对象。他们形成一种图形。他们已经得到了对方的引用。其中一些对象是活的,有的则是不再使用。那么,在GC所做的就是从一组,我们称之为根对象开始。这些是在GC知道对象是活的。


例如,变量是一个线程堆栈上活着,J和我全局引用,我们在对待受精卵对象作为堆,或根为好。因此,基本上,在GC开始与这些对象,并开始访问的其他对象。基本上,在整个图形遍历找出哪些对象从GC根直接或间接引用。在这个过程结束时,你有遗留下来的一些对象,从未访问过的GC。这些都是你的垃圾。它们可以被收集。所以这是一个非常简单的概念。你可以看到为什么当我说,你有你将有较大的暂停时间更大的堆。由于垃圾收集器主要有遍历整个现场设置的对象。如果您使用说出一大堆选项,你有一堆的256兆,嗯,这是一个很大的内存垃圾收集走过去。你会看到与较长的暂停时间。


我们虽然有一些好消息。在姜饼,已经有垃圾回收一些很大的变化,使事情好了很多。因此,在Gingerbre-对不起,预姜饼,垃圾收集器的状况是,我们不得不阻止世界收藏家。所以,这是什么意思是,基本上,当垃圾收集过程中,您的应用程序已停止。而集合出发的所有应用程序线程都完全停止。这是一个非常标准的东西。这些停顿一般往往是相当短。我们发现的是,暂停时间为堆了愈来愈大,这些都逐渐成为一个有点太长了。所以,我们看到东西了50到100毫秒。如果你想建立一个真正响应的应用程序类型的那个停顿时间是不是真的可以接受的。


因此,在姜饼,我们现在有一个并发的垃圾收集器。它大部分工作的同时,这意味着你的应用程序没有停止垃圾收集的时间。基本上,我们有一个是在同一时间为您的应用程序,可以执行垃圾收集工作运行的另一个线程。你会看到基本上是两个短暂停。一次一个集合的开始和一个靠近端。但是,这些暂停时间将是非常非常低。通常你会看到两个,三个,四个,五个毫秒的暂停时间。所以这是一个显著改善。暂停时间关于他们曾经是10%。所以这是我们在姜饼一个非常好的变化。


现在,如果你正在构建内存重应用,则您使用的是大量的位图的一个很好的机会。我们发现,在很多应用中你有可能50或堆的75%是通过位图占用。而在蜂窝,因为你要在平板电脑上进行开发,这变得更加恶劣。因为您的图片更大,以填补屏幕。所以蜂窝前,我们设法位图的方式是这样的。因此,蓝色区域了这里是Dalvik的石堆和黄色的对象是位图对象。现在,位图对象总是在堆的大小相同,无论它们的分辨率是什么。对于位图中的背衬存储器实际上被存储在另一个对象。这样的像素数据分别存储。现在蜂窝之前我们所做的是这样的像素数据实际上是本机内存。它是用在Dalvik堆外的malloc分配。这有几个后果。如果你想释放此内存,你可以打电话或者回收,这将同步释放内存。但是,如果你不叫循环,你在等待你的位图被回收,我们不得不依靠终结,以免费为位图后盾内存。如果你熟悉定稿,你可能知道,这是一个根本不可靠的过程。只是它的性质,它需要几个收藏,通常定稿完成。所以,当你不得不等待几个垃圾收集被开垦的像素数据之前,这可能会导致与位图重应用问题。这可能是很多的内存,因为位图的像素数据是堆的相当一部分显著。这也使事情变得更难调试。如果您使用标准内存分析工具,如Eclipse的内存分析器,它不能真正看到这本机内存。你会看到这个微小的位图对象。当然,但是这并没有告诉大家。如果你有一个10×10位图,你不要介意。但是,如果你有一个512×512的位图这是一个很大的区别。最后,我们有这种方法的另一个问题是,它阻止世界的垃圾收集,以回收内存的支持所需的完全,假设你没叫回收,那是。


好消息是在蜂窝,我们已经改变了这种工作方式。和位图像素数据现在分配的Dalvik堆里面。因此,这意味着它可以同步通过在该位图被收集在同一周期由GC被释放。它也更容易调试,因为你可以看到标准的分析工具,如Eclipse内存分析器这个后盾内存。而且我打算做一个演示在几分钟内,你会看到真正的,如何更加有用,这是当你可以看到该内存。最后,这种策略是更适合并发和部分垃圾收集,这意味着我们通常可以保持这些暂停的次数。因此,这些都是会影响您的应用程序如何使用内存,我们在姜饼已经推出了两个最大的变化和蜂窝。


现在我想在跳水一些工具,你可以用它来更好地了解您的应用程序的使用多少内存。如果你有内存泄漏,更好地了解那里的泄漏是一般,你的应用程序是如何使用内存。您可以使用您的理解应用程序的内存使用最基本的工具是看你的日志信息。因此,这些都是你在DDMS在logcat的视图查看日志消息。你也可以看到他们在使用亚行logcat命令行。而每一个垃圾收集发生在你的过程中的时候,你会看到一条消息,看起来像这一个。我只是想通过这条消息的不同部分,这样你就可以更好地理解它告诉你。


我们的第一件事就是为垃圾回收的原因。是什么触发它,什么样的集合是一种。这里这一个是并发收集。因此,一个并发收集是由基本触发,因为你的堆开始填满,我们踢了我们的并发垃圾回收使之能有希望完成之前,你的堆得到充分。


其他类型的集合,你会在日志消息见。 GC的malloc的就是其中之一。这时候,比方说,我们没有完成并发收集的时间和你的应用程序不得不分配更多的内存时会发生什么。堆得满满的,所以我们不得不停下来做垃圾回收。


你会看到GC外部页头,这是外部分配的内存,就像我提到的位图像素数据。它也可用于直接NIO字节缓冲区。现在,这个外部存储器正如我所说,已经消失在蜂窝。基本上一切都在Dalvik堆里面现在分配。所以你不会看到这个在蜂窝,后来你的日志信息。


您还会看到一条消息,如果你做一个HPROF,如果你创建一个HPROF文件。最后,最后一个我想提的是GC明确。你通常看到这个,当你调用System.gc,这是你知道你真的应该避免做一些事情。一般情况下,你应该在垃圾收集器信任。我们已经得到一些信息,还有关于这个集合释放的内存量。有关于堆的一些统计数据。因此,在这种情况下,堆,是免费的65%的收集完成之后。有现场的对象约三个半兆这里总堆大小列出。这几乎是10兆,9991 K.有一个关于外部分配​​的内存,这是位图的像素数据也,NIO直接字节缓冲区的一些信息。这里的两个数字,第一个数字是你的应用程序已分配的外部内存量。第二个数字是一种软限制。当你分配的内存量,我们将揭开序幕GC。最后,你会看到暂停时间为收藏。这是你会看到堆大小的影响。较大的堆将会有较大的暂停时间。


好消息是,对于并发收集,你会看到一般相当低,这些暂停时间。并发集合会显示两个暂停时间。有在集合的开头的一个短暂的停顿和一个最的方式通过。非并发集合,你会看到一个暂停时间,这是通常会是一个相当高一点。所以看你的日志信息是了解您的应用程序使用多少内存一个真正的基本途径。但它并没有真正告诉你,在这里我使用的内存?什么对象是使用这块内存?


而要做到这一点的最好办法是使用堆转储。因此,一个堆转储基本上是一个包含所有在堆中的对象的信息的二进制文件。你可以通过点击图标,这个有点神秘的图标使用DDMS创建一个堆转储。我认为,在前面的谈话中提到它。还有用于创建堆转储的API。一般情况下,我发现使用DDMS的罚款。还有,当你想在某个时间非常,非常具体的点来创建一个堆转储倍。也许当你试图追踪内存泄漏。因此,它可以有助于使用该API。您可能需要将堆转储转换为标准格式HPROF。您只需要做的,如果你使用DDMS的独立版本。如果您使用的Eclipse插件,在ADT插件,它会自动将其转换。但转换是非常简单的。有一个在Android SDK中,您可以用它来做到这一点的工具。你已经将它转换为标准格式HPROF后,您可以使用标准堆分析工具,如垫或分析与jHat它。


而且我要告诉MAT的例子,这是说Eclipse的内存分析器的短路。之前我跳进演示,我想谈谈内存泄漏。因此,有一种在托管运行时,你不能有内存泄漏误解。我敢肯定,你们知道这不是真的。有一个垃圾回收器不会防止内存泄漏。在托管运行时内存泄漏是有点不同,虽然,比C或C ++内存泄漏。基本上,泄漏是当你有到是防止被垃圾收集的对象未使用对象的引用。有时你可以有一个引用一个对象,但对象指向了一堆其他对象。基本上,该单一参考阻止一大群的对象从收集。


有一点要注意在Android中。我看到有时人们和我这样做我自己,不慎被拿着长寿命参考活动创建一个内存泄漏。所以,你需要非常小心与和也许是你持有到上下文的引用,这是发生了什么。您还可以通过保持长期居住引用一个视图或一个绘制,因为这些也将举行,他们在最初活性的参考做到这一点。而且,这是一个问题的原因,究其原因这会导致内存泄漏是这样的。所以,你有你的活动,它包含了ViewGroup中,一个的LinearLayout什么的,它包含了一些看法。而且我们已经得到了从框架到当前可见的活动的参考。


但是,在Android上,当你有一个旋转活动,让您旋转你的设备,我们做什么,因为你需要加载新的资源实际上是建立一个新的视图层次,你可能有一个全新的布局横向或纵向,你可以有不同大小的图标或位图。然后,我们基本上是删除提及旧观点的层次结构和指向新的。而这个想法是,这个老视图层次结构确保被回收。但如果你持有到一个参考,你要防止它得到垃圾收集。这就是为什么它是保持长寿提及的活动,甚至为视图,因为事实上,连接这些对象的箭头应在两个方向上会出现问题。因为你已经得到了指针一路上涨。所以,如果你有内存泄漏,一个真正的好办法弄清楚其中使用Eclipse的内存分析器。


我打算做一个演示,但我想先介绍一些背后的内存分析器的概念,所以,当我做演示,你会更好地了解我向您展示。所以Eclipse的内存分析器可以从eclipse.org网站下载。它有一对夫妇的口味。这里有一个Eclipse插件的版本,也有一个独立版本。我要上展示的独立版本。我个人不喜欢让Eclipse有所有这些不同的插件。我有点想保持的东西一点点分开。但他们基本上是相同的。现在,内存分析器有,你会看到很多的一些重要概念。


它谈论浅堆和保留堆。因此,一个对象的浅堆是多么大的是这个对象,它的大小和字节。这真的很简单。所以我们可以说,所有这些对象都是100字节。所以他们浅薄堆是100个字节。这很容易。保留的堆是不同的东西。基本上,保留堆说,如果我在这里有一个对象,我是来释放这个对象,还有什么其他的对象是指向?并可能那些同时被释放?所以你计算方面的留存堆,什么是可能通过释放这一个对象被释放对象的总大小?因此,也许这是最好的一个例子就明白了。所以这个对象下来黄色的右侧,这家伙不指向任何其他对象。所以他的保留大小是很容易计算。他保留堆100之上这家伙,他有一个指向另一个对象。但他不持有该对象活着。还有其它指针到该相同的对象。所以,这家伙的保留堆也只有100个字节。因为如果我们要删除此对象,它不会释放任何其他对象。对象降底然而,它基本上保持所有其他对象活着。因此,它保留堆是400,因为如果我们能释放的对象,我们可以释放所有其他对象好了,在这张幻灯片上反正。所以,你可能会想,你怎么去计算保留堆?


所以,你会看到这个内存分析器。而实际上,知道它是如何计算保留的堆是非常有用的。所以内存分析器使用一个称为分母树的概念。这是从图论的一个概念。基本上,如果你有一个节点A和节点B,A被认为是B的支配者,如果到B每一条路径经过A.所以你可以看到如何可以帮助我们弄清楚一个对象的保留堆是。所以这里的另一个例子。因此,让我们先从A.这是一种根本的。 B和C都只能通过A.访问,所以它是非常简单的。他们是儿童和支配树。 E是也只能通过访问C.所以它的C在支配树一个孩子。 D是一点点有趣的在这里。 D可通过B或C访问,但是A是每一个路径D.对这样就意味着A是D的家长和支配树。而现在你会看到这个支配树概念还弹出的内存分析器在它的UI。而且它可以为跟踪内存泄漏真的很有帮助。


因此,让我们跳和做调试和内存泄漏的演示与MAT。那么,我要使用这个演示是蜂窝画廊的示例应用程序。它自带了Android SDK的基本上只是展示一些蜂窝功能的简单应用程序。真的,全部是是一个小应用程序中,您可以通过网页一些照片。很简单。现在,我已经在这里做了那种调皮。我介绍了内存泄漏到这个应用程序。我会告诉你我是如何做到这一点。对不起,我又更好切换到幻灯片。


所以,你会看到这里我的源代码,请从活动的源代码的摘录。所以我在这里所做的是我已经介绍了这家名为泄漏内部类。这是不是一个静态内部类。所以,你可能知道,如果你创建一个非静态内部类,它实际上一直到封闭对象的引用。这是因为,从一个非静态内部类,你其实可以参考封闭对象的实例变量。因此它打算在这里保留活动的参考。只要此对象不活得比活动这很好。但我有这个静电场和静过得比任何特定实例更长的时间。而在我上创建方法,我做了什么被实例化泄漏对象,并将其存储到静态字段。所以,如果你想成为能够想象这一点,我基本上得到了与主活动开始我的看法层次。我这个实例化对象漏水,他不得不主要活动的参考,因为这是它的封闭类。最后,我的主要活动类,这是概念上的内存比任何特定实例的不同区域。而且还有一个静态变量指向泄漏对象。因此,也许你可以看到这是怎么回事造成内存泄漏,当我旋转屏幕。因此,让我们跳,并看看此内存泄漏。所以,如果你想弄清楚是否有内存泄漏,最简单的方法之一是正中下怀看看你的日志信息。所以我只是要做到这一点。我要在命令行中做到这一点。我可以只输入logcat的。我想它限制,我已经来到这里运行的特定进程。我不希望看到所有的日志信息的系统。所以我只是要抢的进程ID。在那里,我们看到一堆日志信息,包括一些垃圾收集信息。你想看看数基本上是在这里的9805K的第一个数字。在堆大小的第一个数字。这是在系统中的活动对象的量。如果你正在寻找一个内存泄漏,这就是你想看看是什么。所以我要通过这里的一些照片翻转。你会看到这个数字保持不变漂亮。我们到9872.但基本上,堆的使用量是相当恒定的。现在,当我转动这个设备,我们将成为一堆垃圾收集的发生。这堆使用上升,它不会再次下井。所以,我们堆到现在为止12兆。因此,我们对泄露两年半兆。所以,当你看到你的记忆中有点像一个阶跃函数的上升,其步骤和公正永远不会退缩,这就是你有内存泄漏一个好兆头。


所以,一旦你知道你有泄漏,你会想要做的就是创建一个堆转储,所以你可以去有关调试它。所以我要做到这一点现在。我就开了DDMS。你只需要选择您所关心的进程,然后点击上面写着转储文件HPROF工具栏中的这个图标了。这将创建一个堆转储。因为它基本上是倾倒了巨大的二进制文件到磁盘它需要几秒钟。然后,我就可以把它保存在一个名为dump.hprof文件。然后,因为我使用DDMS的这种独立版本在这里,我需要这个文件转换。正如我所说,如果你使用了ADT插件的Eclipse,并在那里使用DDMS,你并不需要经过这个转换步骤。但它真的很简单。现在,我已经转换它,我可以打开Eclipse的内存分析器,并看看这堆转储。所以没有太多的内存分析器看到,直到你已经打开了一个堆转储,我们可以直接从文件菜单来完成。打开堆转储。我会打开这个转换堆转储,这是我刚刚创建。它加载了不走很长。


你会首先看到的就是这个饼图。这是通过保留尺寸示出在系统中的最大的对象。现在,这本身并不能真正告诉我们太多。你可以看到,倒在左下角这里,当我鼠标移到饼图的各种片,它告诉我,我有什么样的对象。但是,这并不真正告诉我们太多。如果我们想获得更多的信息,你想在这里往下看。有两种观点。直方图视图和支配树。这些都是我觉得最有用的,我要告诉你的人。让我们来看看支配树。你还记得我解释的概念。这怎么能说是在跟踪内存泄漏是有用的。所以,我们现在看到主要是实例的列表或对象在组织本系统的列表。这里有一列。通过保留量堆组织。所以,当你有一个内存泄漏,看着保留堆量往往是看的事情,因为那将有你使用多少内存的最大效果的好方法。机会是,如果你已经注意到,你有一个泄漏,你就渗出了显著量。所以,让我的Xoom在这里了。希望你们能好一点看到这一点。因此,在该列表的顶部我们拥有的资源类。这不是太奇怪,因为我们的资源,我们需要加载大量的位图。这是怎么回事持有大量内存活。没关系。这两个位图对象是有趣。我有这两个大的位图,每兆超过两年半。这很有趣,因为这就像我被泄漏的内存量听起来是。所以,如果我想查远一点,我可以用鼠标右键点击其中的一个对象,然后选择路径的GC根。我会选择不计,因为我想看看有什么保持该对象活着弱引用。和弱引用是不会让它活着。因此,这开辟了一个新的选项卡,你怎么知道?它实际指向正确的我的泄漏。因此,当你创建你的应用程序泄漏,确保你的名字像这样真正有用的,所以你可以很容易地找到它。


听众:[笑]


PATRICK DUBROY:所以你们当中有些人可能已经注意到了这一点,如果有只是对这个对象的单一路径,因为这就是我可以在这里看到,为什么没有这样的泄漏对象在支配树显示?我提到支配树应留存堆量表明您最大的对象。和以及这是一个单一的对象,该对象是负责保持该位图。因此,对于其原因是Eclipse的内存分析器,当计算支配树,它实际上并没有分开处理弱引用。它基本上只是将它们视为一个正常参考。所以你会看到,如果我真的对上这家伙再次点击,并说路径GC根,并与所有引用说,那么实际上是对这个对象另一条路径。但它是一个弱引用。一般来说,你不必太在意弱引用,因为他们不会阻止你的对象被垃圾收集。但是,这就是为什么泄漏的对象并没有在支配树出现。所以支配树是一个人真正,跟踪内存泄漏的真正有用的方法。我喜欢用另一件事是直方图视图。所以我提到,在Android中,这是常见的通过保持长期居住引用的活动内存泄漏。所以,你可能想实际去看看,你有你的主要活动类的实例数。


与直方图视图可以做到这一点。所以柱状图查看只是表明在其系统中的所有类的列表,现在它是基于浅堆由类系统占用的总额来分类。因此,在最高层那里,我们看到我们有字节数组。并且这样做的原因是,字节数组现在是像素数据的支持存储器。你知道,这就是为什么它是非常有用的,我们现在有堆内部的像素数据的一个很好的例子。因为如果你使用这个姜饼上或更早,你不会看到字节数组在顶部。由于在本机内存中分配的内存。所以,我们也可以,如果我们关心这些字节数组对象,我们可能想右击它,并说与传入引用列表对象。而我们现在看到我们的两个大字节数组对象。我们可以用鼠标点击一个,说,道路GC根,不含弱引用。原来这家伙看起来有几条路径,这让它活着。看起来没有什么与众不同的我。而当你试图找到内存泄漏,但不是一个真正的神奇的答案你是怎么找到泄漏。你真的了解你的系统,并了解哪些对象是活的,为什么他们还活着,你的应用程序的各个部分中。但是,你看看我看看这个其他字节数组对象,并再次,做路径GC不含弱引用的根,好了,我又找到了我的泄漏。所以这是我可能已经发现了这一点,如果它不是来自支配树那么明显了另一种方式。



直方图视图还可以帮助我们寻找我们的活动情况。所以有很多的类显然在系统中。我们的活动是不是在这里。有2200班。但幸运的是,Eclipse的内存分析器在顶部有这个方便的小过滤器视图。你可以开始输入一个正则表达式。它会回报你的所有匹配的类。所以在这里,我们有我们的主要活动。它告诉我们,其实有这个主要活动的两个实例。这应该一种是红旗。通常情况下,你应该会看到只有你的主要活动的一个实例alive.Now我屏幕旋转时所提到的,我们建立了一个新的视图层次,还有的将是一个短暂的时间,其中有两个实例活着。但在大多数情况下,你应该会看到一个在这里。所以,我可能会想,OK,这是一个危险的信号。


让我们来看看。所以,我可以用鼠标右键单击这个对象,并传入引用对象列表。所以,我想看看我有什么情况下,什么是指向他们?所以,我在这里有两个实例。如果我对他们中的一个右击并选择路径GC根,不含弱引用,我又发现我的内存泄漏。而在看这个,我可能会意识到,哦,我真的不打算这样做。我不是故意要保持这种参考那里。不过就是这样,你可以找到泄漏的另一种方式。所以,现在,我们已经发现了我们的地方内存泄漏是,我们为什么不真的去前进,并修复它。所以在这种情况下,问题是,我们有一个非静态内部类。因此,我们可以通过一个静态内部类解决这个问题。然后它实际上不会保持封闭活动的参考。我们可以做的另一件事其实只是没有将其存储在一个静态变量。所以它的细如果此泄漏对象具有活性的参考,只要它不寿命比活性。因此,让我们做到这一点。让我们眼前这个让一个普通实例变量,而不是静态的。于是我可以去这里重新编译这一点,它推送到设备。并希望,我们应该看到,我们的内存泄漏已被淘汰。很抱歉,我们真正想要做的是看看我们的日志输出才能看到我们使用多少内存。所以我只是要在这里火起来的过程中,看看进程ID。再次,只是做亚行logcat只是这一进程。因此,正如我通过照片再次页面,我们看到了很多GC的消息。当我旋转,我们要看到内存使用率上升一分钟出现。但经过几集,但它去回落到先前的值。因此,我们已经成功地消除了泄漏那里。这是伟大的。你总是想消除内存泄漏。


这就是使用Eclipse内存分析器来调试内存泄漏的一个例子。 Eclipse的内存分析器是一个非常强大的工具。这是一个有点复杂。它实际上是我花了相当长的一段弄清楚这些是这个职位的两个最好的工具。所以,你真的要当心这些内存泄漏。所以,我在这里保留了长寿命参考活动的举了一个例子。如果你有我们的语境,视图,绘制,所有这些事情,你需要提防。不要拿着那些长期生活引用。它也可以发生非静态内部类,这是我证明有作为。实际上可运行的是一个有时会咬你。你知道,你创建一个新的可运行。你有那将到五分钟运行一次的延期事件。如果用户旋转递延可运行屏幕将举办以前的活动实例活着五分钟。所以这是不好的。


您还需要注意的缓存。有时候,你有一个缓存,并要保留记忆活着,这样就可以加载图像更快地让我们说。但是你可能无意中活着太久装东西。所以这基本上涵盖,Eclipse的内存分析器的核心部分,并为您提供了内存泄漏的一个基本的了解。如果您想获得有关的内存分析的更多信息,下载链接,你可以找到在eclipse.org/mat网站。马库斯·科勒谁是Eclipse的内存分析器的原创团队成员之一,他有一个叫做Java性能博客博客。这实在是太棒了。他有很多很好的文章上有关于MAT,你可以用它来了解您的应用程序的内存使用不同的方法。


我也有我的Android应用程序在Android开发者博客称为内存分析写了一篇文章。它涵盖了很多,我在我的演示在这里做同样的东西。而罗曼盖伊也有关于避免在Android的内存泄漏的好文章。所以我希望这一直是有帮助的,我希望你们现在的你如何找出你的应用程序的内存使用情况有更好的了解。


我已经谈到了两个会影响您的应用程序如何使用内存,我们在姜饼所做的最大的变化和蜂窝。谢谢。


[掌声]


所以,我可以从地板的问题,如果任何人有任何。或者,你都希望走出去,去酒吧和有啤酒吗?


听众:嗨,你提到如果您在使用蜂窝NIO你的对象将是不在本机内存,现在他们要来管理内存。如何影响性能,如果你正在做一个NIO,是将成为任何慢,像网络很激烈?


PATRICK DUBROY:不,我的意思是它不应该影响。所以,我应该说,还是有分配本机内存为您的NIO字节缓冲区的方法。我不是那熟悉的NIO接口,但我相信有一个在JNI你可以分配自己的记忆的一种方式。因此,在这种情况下,你仍然要使用本机内存。但无论哪种方式,它只是记忆。它只是分配在不同的地方。因此,有没有什么让Dalvik的堆内存比其他内存要慢。


听众:所以你说如何在蜂窝位图存储在Dalvik的堆,但在以前的版本中,它被存储在本机内存。这是否意味着位图有不同的量堆大小?或者是,仍然都在16或24兆字节计数以前的版本了?


PATRICK DUBROY:是的,很好的问题。会计限制仍然是相同的。这是占了前面。如果你曾经跑进你的堆限制,你可能已经注意到了,你会看着你的堆大小和样子,我还没有达到最大极限,然而,为什么我要走了的记忆是什么?这实际上是占了,所以这是你的总堆大小加上外部分配的内存,这是你的极限量。使没有改变。


听众:你好。我在做垃圾收集踢时的问题。是是,当内存中对象的数量或堆的大小?


PATRICK DUBROY:嗯,这取决于你在谈论什么样的垃圾收集。并发垃圾集电极


听众:是的,并发。是。


PATRICK DUBROY:是啊,所以,我认为是基本量,你的堆是如何充分得到。


听众:因为我注意到,当你做了很多的[听不清]提供的操作,让你有一个像[听不清]操作的列表,在垃圾收集踢但实际上不收取任何对象,因为你只是填补英寸数组要插入到数据库中的对象。这是相当迅速成长。而这往往一点,实际上并没有解决任何堆大小的应用程序变慢。


PATRICK DUBROY:是的,我不知道如果GC看起来AT-所以你基本上是说,我猜,这集是踢它实际上不是能够收集到任何东西,所以它shouldn't-


听众:但它不停地尝试。


PATRICK DUBROY:是的,它应该是足够聪明。是的,我不相信我们居然看那些类型的统计还没有。但我的意思是它似乎是合理的。是啊。


听众:我想知道,如果你们有做一个分析器应用程序或更多的工具来分析内存和所有的东西一些计划?


PATRICK DUBROY:没有计划,我知道的。有没有什么特别的,你需要?我的意思是我认为Eclipse的内存分析器是一个非常强大的工具,我用它在我的一天到一天的工作相当多。所以,我当然从来没有发现它它缺少我需要某些功能。


听众:是的,可能是因为有一些旧版本在Android,显示内存泄漏什么的。但对于例如,在巧克力慕斯蛋糕,有一些东西使用 - 的东西在那里。


PATRICK DUBROY:是的,我的意思是,我们没有任何计划立即,我不认为到运行特定的工具。


听众:好的,谢谢。


PATRICK DUBROY:哦,对不起,我曾是─是的。


听众:据我了解,位图存储器的本地部分是之前实际上是Skia的库的情况下,Skia的库位图类别之一。因此,这仍然存在,或者它现在已经没有更多的本机内存分配?


PATRICK DUBROY:没有,Skia的依然是这堆存在的一部分。基本上在哪里Skia的叫一声分配内存的时候,我们实际上只是回调到虚拟机,分配内存有,而不是调用malloc。所以它仍然是基本相同的机制,但内存只是从不同的地方来了。


听众:OK。听众:我认为,当我用我的申请,我查堆大小。在使用应用程序堆大小不显著上升。但是,这是在应用程序选项卡下运行的应用程序中列出应用程序所使用的内存量,则显著上升。有时甚至翻倍。我知道,这是一个不同的堆时显示在那里。它实际上是进程堆,对不对?你能告诉我什么的背景是,这显示存在,因为可能会喜欢 - 我没有内存泄漏和用户抱怨我的应用程序内存泄漏。因为对于用户而言,它看起来像它的内存泄漏。


PATRICK DUBROY:对。因为你说有这是由于你的过程,表现出对任何─基本上起来,在系统内存中的东西?


听众:是的。所以,它显示在应用程序选项卡,这是不是真的链接到我的堆内存的系统内存。所以这是怎么回事,但我只能控制堆内存。如果我没有本地应用程序我无法控制一切。


PATRICK DUBROY:我的意思是要在该会得到更大的系统的各种事情。例如,喜欢你的JIT代码缓存。随着JIT踢和被分配内存,就像它需要在一些地方保存编译后的代码。因此,有一定的分配这回事那种得到充电到您的应用程序内存此系统的其他部分。但我想不出为什么。我想不出任何东西,这将是不寻常真正应该引起问题。


听众:但是你知道这是否会在将来有变化?这是不是有这个显示的数字,因为对我来说,没有任何意义,以显示这个号码给最终用户,因为他不明白是什么意思。


PATRICK DUBROY:我明白了。他在那里看到了多少?


听众:在运行的应用程序选项卡。如果他去设置,运行应用程序,他可以看到每个应用程序的内存使用情况和实际的系统内存。


PATRICK DUBROY:我明白了。是啊,我不知道我们的计划是这一点。抱歉。我可以看看,我实际上并不知道在那里它得到的数量从。


听众:好的,谢谢。


听众:我的问题的有关内存不足错误的合理预期。是否有可能完全消除它们?我们已经在大约每17000会话已经工作了,而在摆脱所有的内存不足错误的,下一个。我们应该把故障排除。我的意思是,我想它弄下来到零,但是是合理的还是?


PATRICK DUBROY:那么在有些情况下,如果你真的接近你的内存限制某些情况下,因此,如果您的应用程序实时内存大小非常接近该限制,垃圾收集器的根本一种异步的。所以,如果你真的接近极限,可以有时间在这里你只是试图分配如此之快,垃圾收集器无法跟上。所以,你可以真正样的跑出来的垃圾收集器。所以肯定有可能构建一个从来没有看到一个内存不足的错误应用。但在另一方面,也有某些类型的应用程序将要真的不多,非常接近极限。有一件事,如果你有缓存或东西,你可以使用你可以释放,有几种方法搞清楚,你越来越接近堆内存的限制。我相信有回调就可以得到我们正在对存储空间不足的通知。虽然名称脱离了我。但你也可以看一下,活动管理,获取内存类来获取你有多少内存在系统上可用的感觉。你知道,也许你可以保持像小高速缓存或将初始化对象,而不是初始化他们都在构造函数或类似的东西。这真的取决于你是否想到会接近堆限制或不运行应用程序。


听众:你建议不要手动调用System.gc如果你能帮助它。有什么办法能够可靠地自由位存储器预蜂窝?


PATRICK DUBROY:是的。预蜂窝?


听众:是的。


PATRICK DUBROY:您可以拨打回收的位图。


听众:是的,但是它仍然需要数遍明显。


PATRICK DUBROY:不会。如果你调用回收,将立即释放后盾内存。位图本身,这跟80个字节或东西。


听众:也有位图图形内容一样,你不能手动回收的绘制对象创建位图。


PATRICK DUBROY:OK。


听众:背位图的。


PATRICK DUBROY:我明白了。不,我的意思还是有一些情况下,我猜是哪里的System.gc是正确的做法。


[不知所云PHRASE]


PATRICK DUBROY:好的,哪些对象是你在说什么IN-


听众:我的经验是,当我有使用一些地方在我的布局可绘制的图像,我知道他们不再需要。其中有些是相当大的,它似乎喜欢 - 


PATRICK DUBROY:您可以拨打回收这些我相信。


听众:OK。我的经验是,它会导致其他问题,当我做到这一点。


PATRICK DUBROY:如果你还在使用它们,那么你就不能 - 我的意思是,你只能回收,当你不使用它。


听众:当然。好。


听众:对于使用了大量mallocs的原生代码,什么是管理内存的最好方法?


PATRICK DUBROY:这是一个非常好的问题。当你有原生代码,我的意思主要是我在这里涵盖了从事物的Dalvik的侧管理内存。我不知道我有什么真正的指针。我的意思是这的原因,节目的管理运行是非常好的之一。是,你不必应付手动管理你的记忆。我没有对任何伟大的建议。


听众:这是否调用到机库的应用程序,它是知道的,至少在一个水平加剧,多少内存正在使用或者是它完全是separate-


PATRICK DUBROY:我不相信有什么办法来解释,如果你打电话到图书馆,它调用malloc。我不知道,有什么办法可以考虑从应用端内存。


听众:但是,垃圾收集器会在您启动分配内存运行时,它不会呢?


PATRICK DUBROY:当你开始分配像Dalvik的对象,它会运行。它不必调用malloc的任何知识。


听众:你只是得到一个内存或失败的malloc如果你 - 


PATRICK DUBROY:是的。当然。这将是相同的机制作为任何C或C ++程序。的malloc会返回一个空指针。是?


听众:[不知所云PHRASE]


PATRICK DUBROY:对不起?


听众:[不知所云PHRASE] PATRICK DUBROY:哦,好。这消息给我。的malloc不能不在Android上。


听众:[不知所云PHRASE]


PATRICK DUBROY:我明白了。好。


听众:你能再说一遍吗?


PATRICK DUBROY:罗曼告诉我的malloc不能在Android上失败。


听众:[不知所云PHRASE]


PATRICK DUBROY:我明白了。所以我觉得这是一个旧的Linux lazy-呀。它会成功分配虚拟内存,但事实上Linux可以把手伸到比它实际上可以投入更多的虚拟内存。所以,你可以得到的问题。当你的系统是完全,完全是出于本机内存一样,你会看到崩溃。


听众:所以本机内存是什么Dalvik的完全独立?


PATRICK DUBROY:是的。嗯,我是说,对不起,我应该说,类似的Dalvik仍然分配自己的内存像通过本机机制堆。因此它保留了其他应用程序所使用的相同的虚拟内存页。


听众:但如果你的系统内存是 - 


PATRICK DUBROY:是的,如果你的系统内存时,你就有麻烦了。


听众:不过的Dalvik不会得到通知说,哎,最好开始垃圾回收?


PATRICK DUBROY:哦,不。


顾客:使用更大的堆标志,确实需要一个权限,比如用户许可或类似的东西?


PATRICK DUBROY:我不记得我们是否补充说与否。我不认为它。


听众:像时的全可它一直像一个权限的事情吗?但是,如果不是,则─


PATRICK DUBROY:是的,我的意思是,我认为这个想法是觉得─是的,你说得对。我的意思是它会影响系统作为一个整体,因为你将有一个使用了大量的内存的应用程序,这就是为什么我给那个大的警告,这是不是你应该使用,除非你知道你真的需要它。


听众:是的。但是,[听不清]。好。


PATRICK DUBROY:我不认为有它许可,但。


听众:如果有什么应用程序在一个时代背景几周样的运行?所以我尽我所能来模拟泄漏,点击无处不在我可以,但我看到了泄漏如果应用程序运行两三天,然后我得到的[听不清]。


PATRICK DUBROY:有一件事你可以尝试是,如果你可以使用API​​来决定你有多少可用内存有。我不知道是否有什么办法可以竟是那样的应用程序,它开始漏水通知。但是,当你发现,你已经得到了一定的时候,你的堆越来越小,你可以写出一个HPROF文件。因此,有一些调试信息那里,你可以使用。所以,如果你有一个像一些测试,谁实际上可能会给您发送这些垃圾场,那么你可以做到这一点。所以写出来的HPROF文件到SD卡或东西。


听众:所以也许我可以只写一个HPROF文件任何─


PATRICK DUBROY:我不会那么做的。我的意思是他们是相当大的。你不想那样做定期。但是,如果检测到事情已经过去了真的,真的错了,你即将死去,在一个alpha版本或某事进行测试这是你可以做到这一点的方法之一。但是我绝对不会建议把一个应用程序在倾倒的像非常大的文件,没有理由SD卡的市场。


听众:OK。


PATRICK DUBROY:好的,非常感谢。

0 0
原创粉丝点击