性能测试

HotSpot虚拟机内存管理

2017-04-13  本文已影响40人  tianyiliusha

第一章 简介

J2SE平台的一大优势是它的自动化内存管理,避免了开发者去面对内存管理的复杂性。

本文以Sun J2SE 5.0的发行版为例提供对HotSpot虚拟机的内存管理的一个宽泛的综述。文章描述了用于内存管理的GC(Garbage Collector),并对如何选择和配置GC,如何配置GC处理的内存区域大小等给出建议。本文也可以作为一种资源,其中列举了大量常用的影响GC行为的选项,以及一些索引到更详细文档的链接。

第二章节主要面向对自动内存管理概念不太了解的新人。其中包含一个简短的论述,关于自动内存管理和程序员手动内存管理的利弊。

第三章会呈现一个对GC的概念、设计选择、执行指标的综述,也会介绍一种常用的基于类的生存时间将内存划分为不同区域的组织方法,被称为“分代”。分代的隔离性已经证明其在降低GC暂停时间和应用整体耗时方面的高效。

文章剩余部分提供对HotSpot虚拟机信息的说明。第四章描述4种可用的GC,其中一个是在J2SE 5.0第六次更新中新引入的,同时整理了所有GC都使用的“分代”内存组织方法。第四章对每一种已经使用的回收算法的类型进行总结以及说明应该如何选择合适的GC。

第五章描述了一种J2SE 5.0新的技术,综合了动态GC选择,堆管理,基于应用运行平台和操作系统的HotSpot虚拟机,根据用户期望行为的GC动态地切换等多个方面。这项技术被认为是工效学的应用。

第六章提供选择和配置GC的建议,以及处理OOM(Out Of Memory)的忠告。第七章简要的描述了一些可用于评估GC能效的工具。第八章列举了常用的命令行工具,用于GC的选择和行为。最后,第九章提供了一些链接,索引到本文覆盖一些主题的更详细的材料。

第二章 直接内存管理与自动内存管理

内存管理就是一个识别的过程,识别已分配内存的类何时不在需要,何时释放其占用的内存,以及何时为后续的内存分配标记其是可用的。在某些语言中,内存管理是程序员的责任。其复杂性会导致许多常见的错误,引起难以预料的程序行为甚至崩溃。正因为如此,开发者会花费大量的时间调试和修复这类错误。

直接内存管理会发生一种常见的问题是“悬挂引用”。给一个类分配一块另一些类仍然在使用的内存区域是很有可能的。如果悬挂引用的类尝试访问类实例,而其空间已经分配给新的类了,那么结果将不可预料,也不知道将会发生什么。

另一个常见的直接内存管理的问题是“内存泄漏”。如果已经分配的内存不在被引用,而又没有释放,内存泄漏就会发生。举例来说,你打算给释放一个链表的内存空间,然而在访问第一个元素的释放,发生了错误,那么剩余的链表元素也就不在被引用,但它们已经不能被程序访问,也不能被使用或者覆盖。如果内存泄漏发生的足够多,那么它们会持续侵占内存,直到内存耗尽。

一种内存管理的替代方案就是利用具有自动化内存管理特性的GC程序,其被大量现代面向对象的编程语言所采用。自动化的内存管理提升了接口的抽象能力和代码的可靠性。

垃圾回收器避免了悬挂引用的问题,因为正在使用得类是不会被当做垃圾回收的,也就更不会被释放。垃圾回收器也能解决内存泄漏的问题,因为它会释放掉所有不在使用的内存区域。

第三章 垃圾回收器的概念

垃圾回收器的工作是:

  1. 分配内存
  2. 确保任何有引用的类都是在内存中
  3. 收回代码中不再使用的类所占用的内存区域

存在引用的类,通常会说成是“存活的”。不再被引用的类会被认为是“死的”,术语称作是垃圾。发现和释放那些内存区域的程序,被称作垃圾回收器。

垃圾回收器可以解决很多,但不是所有的内存分配问题。举例来说,你可以无限制的创建类,持续的引用它们,直到内存耗尽。垃圾回收器本身也是一个耗费时间和资源的任务。

垃圾回收器采用精准的算法来识别、分配和释放内存,并隐藏起来不被程序员发现。分配的内存空间通常来自于一个被称为“堆”的内存池中。

垃圾回收的耗时取决于垃圾回收器本身。典型的情况是,当堆满了或者达到一定的占用比例阈值,整个堆或者一部分会被回收。
完成内存分配请求的任务,涉及到查询一块特定大小而又可用的内存区域,是比较困难的。动态内存分配的主要问题是避免碎片化,同时保持高效的内存分配和释放。

期望的垃圾回收特性

垃圾回收器必须是安全和全面的。存活的数据绝对不能被释放,而垃圾不应该在多次回收循环后仍然停留。

垃圾回收器处理高效,没有引入长时间的暂停,因为暂停期间应用是不能运行的。然而,像大多数类计算机系统,通常需要在时间、空间、频率上进行折中。举例来说,假如堆很小,回收就会更快,但是堆也会更快的填满,也就需要更快的回收频率。相反,一个大的堆将花费更久的时间填满内存,同时回收的频率也就更慢,但是回收也就要消耗更多时间。

另个一个期望的特性是限制碎片的数量。当垃圾类的内存被回收,释放的空间可能会零散的分布在多个区域,那么就可能没有足够的内存为一个大对象分配一个连续的区域。消除碎片的一个方案被称为“压缩”,在下面的不同垃圾回收器的设计选择中将会讨论。

扩展性也很重要。对于多核系统上的多线程环境,内存分配不应该称为瓶颈,同理回收也是如此。

设计选择

当设计和选择一个垃圾回收算法时,需要作出如下的抉择:

能效指标

有很多指标可以衡量垃圾回收器的运行能效,包括:

一个交互式应用可能要求低的暂停时间,而对于非交互式应用,整个的执行时间更加重要。一个实时的应用希望在任何阶段垃圾回收暂停时间和回收的时间都有一个较小的上界。对于个人计算机或者集成式系统,使用小的内存是最主要的关注点。

分代垃圾回收

当一个被称为分代的技术被使用时,内存区域将会被分成不同的带,存储不同年段的类。举例来说,最广泛采用的分代配置方法是:年轻代和老年代。

不同的带中会采用不同的垃圾回收算法,每种算法会根据不同带的特性进行优化。分代回收具有以下特性,被称作“弱分代理论”,无论任何语言写的程序,包括java:

年轻代的回收会频繁发生,高效而迅速,因为年轻代通常较小且更有可能存储大量不再被引用的类。

在多次年轻代的回收中幸存的类,将会被提升到老年代。如图1,老年代通常会大于年轻代,它的容量也会增长的更慢。正因为如此,老年代的回收频率是比较低的,回收的时间也会比较长。

图片 1.png

对于年轻代的垃圾回收算法更关注于速度,因为年轻代更加频繁。对于老年代的算法更关注于空间效率,因为老年代占用大部分的堆内存,其算法不得不处理好低垃圾密度的回收。

第四章 J2SE 5.0中HotSpot虚拟机的垃圾回收器

在J2SE 5.0的第6次更新中,HotSpot虚拟机包括4个垃圾回收器。所有的回收器都是分代的。本章节将描述分代机制和回收器类型,讨论为什么内存分配通常是快速而高效的,同时也会提供每种回收器的详细信息。

HotSpot分代

HotSpot虚拟机中内存被分为三个代:年轻代、老年代、永久代。大部分类初始化时都会被存储在年轻代(较大的类可以直接存储在老年代)。老年代存储着一些经过若干次年轻代的回收依然存活的对象,以及一些直接分配在老年代的较大对象。永久代存储一些JVM很方便进行垃圾管理的对象,如描述类和方法的对象,以及类和方法本身。

图片 2.png

年轻代包含一个Eden区和两个Survivor区,如图2所示。大部分类初始化都被分配在Eden区。Survivor区存储着至少在一次年轻代回收中幸存的对象,它们在被提升到老年代之前还有一些额外的幸存机会。任何时候,Survivor区中都有一个存储着对象,另一个是空的,直到下一次回收。

垃圾回收类型

当年轻代填满,年轻代的回收算法(minor GC)将执行。当老年代或者永久代填满时,著名的full GC(major GC)将会执行,那时所有的分代将会被回收。通常,年轻代将会先回收,使用针对年轻代设计的回收算法,因为它通常在确定年轻代的垃圾上更加高效。然后是使用该回收器上针对老年代的回收算法,回收老年代和永久代。如果有压缩,那么所有代都会单独发生压缩。

有时,如果年轻代先回收,老年代又太满而不能接收来自年轻代向老年代提升的类。这种情况下,所有的回收器除了CMS,年轻代的回收算法将不在运行。代替的是老年代的算法将会用在整个堆上。(CMS的老年代回收算法是个特例,因为它不能回收年轻代)

快速分配内存

在下面对垃圾回收器的描述中,你将会看到在许多情况下会有大量的临近内存块可以用于内存分配。使用空闲指针的技术,在那些内存块上进行分配是非常高效的。上一个分配内存的对象的结束点将会被跟踪(内存被跟踪)。当一个新的分配请求需要满足时,所有被处理的请求都会检测代内剩余的空间是否能够满足需要,如果满足,就会更新指针,初始化类对象。

对于多线程的环境,分配操作需要是线程安全的。如果使用全局锁来进行保证,内存分配将变成一个瓶颈,降低运行的效率。代替,HotSpot虚拟机将采用一项新的技术,被称为TLAB(Thread-Local Allocation Buffers)。这项技术通过给每个线程分配一个自己的缓存池(代中很小的一部分),改进了多线程分配的吞吐量。因为在每个TLAB中仅有一个线程可以分配,分配通过利用空闲指针技术快速进行,不需要任何锁操作。仅在很低的频率下,如果一个线程填满了自己的TLAB,就必须进行同步操作。由于TLAB的使用,有多项缩小空间消耗的技术被应用。举例来说,分配器平均只给TLAB分配低于Eden区1%的内存。TLAB和使用空闲指针技术的线性分配混合使用,加强了分配的高效性,仅需要10个左右的原生指令。

Serial回收器

在Serial回收器中,年轻代和老年代都是串行回收的(使用单个CPU),以一种停止等待的方式。回收进行时,应用将会暂停。

  1. 年轻代回收会用Serial回收器
        如图3所示,年轻代使用Serial回收器。Eden区存活的对象将会复制到空闲的Survivor区,在图中被标记为To,不包括那些太大而不能放进To的对象。那些对象会被直接放到老年代。在From的Survivor区,仍然是相对年轻的对象,将被复制到To的Survivor区,而相对老的对象将会被复制到老年代。注意:如果说To区变满,那么Eden区和From区仍然存活的对象将被转移到老年代,无论它在多少轮的年轻代回收中幸存。在存活的对象被拷贝出去之后,按照规则来说,Eden区和From区剩余的所有对象都不是存活的,也不需要被检测。(X标记的是垃圾对象,实际上回收器并不会去检测和标记它们)


    图片 3.png

        在年轻代的回收完成之后,Eden和先前占用的Survivor区将会变成空的,仅有先前空的Survivor区保留一些存活的对象。某种意义上来说,Survivor区采用交换原则,如图4。


    图片 4.png
  2. 使用Serial回收器回收老年代
        在Serial回收器中,老年代和永久代的回收采用的是一种叫作mark-sweep-compact的算法。在标记阶段,回收器确认哪些对象仍然存活着。在交换阶段,对整个代的垃圾进行交换。然后,回收器会执行移动压缩,移动存活的对象到老年代的开始区域(永久代类似),所有剩余的连续空间被留在相反的区域。如图5所示。压缩允许使用快速的空闲指针技术在老年代和永久代中进行内存分配。

图片 5.png
  1. 何时使用Serial回收器
        Serial回收器是大部分运行在client模式机器上的应用的选择,它们没有低暂停时间的需求。在当今的硬件环境下,Serial回收器高效的管理着许多重要的具有64MB堆的应用,相对于full回收,也减少了糟糕示例的暂停时间。

  2. Serial回收器选择
        在J2SE 5.0发行版中,Serial回收器在一些非Server模式下机器是作为默认的垃圾回收器,就像在第五章描述的那样。在另外一些机器上,Serial回收器要求一个严格的命令行参数-XX:+UseSerialGC。

并行回收器(Parallel Collector)

如今,许多Java应用都运行在大内存和多CPU的机器上。Parallel Collector又称为高吞吐回收器,其开发正是为了充分利用多CPU,避免使用一个核心进行垃圾回收时,其他的核心空闲。

  1. 年轻代使用并行回收器
        并行回收器在年轻代使用的回收算法是一种Serial回收器在年轻代使用算法的并行版本,是一种停止等待和复制的回收器,但是是以并行的方式在执行回收,使用多个CPU,降低垃圾回收的消耗,增加应用的吞吐量。如图6所示,Serial回收器和Parallel回收器的不同。
图片 6.png
  1. 老年代使用并行回收器
        在并行回收器中,老年代的垃圾回收仍然使用串行回收器的串行mark-sweep-compact算法。

  2. 何时使用并行回收器
        能够收益于并行回收器的应用是那些可以运行在多CPU环境,且并没有暂停时间限制,老年代的回收仍然会发生,因为并不频繁,所以潜在可能要更长时间。并行回收器适用的应用是批处理,入账,工资计算,科学计算等。
        你可能会考虑运用并行压缩回收器到整个并行回收器中,因为前者可以在所有的代上执行并行回收,而不仅仅是在年轻代。

  3. 并行回收器的可选项
        在J2SE 5.0的发行版中,并行回收器是作为Server模式机器上的默认垃圾回收器。另一些机器中,需要声明-XX:+UseParallelGC命令才能使用并行回收器。

并行压缩回收器(Parallel Compacting Collector)

并行压缩回收器是在J2SE 5.0的第6次更新中提出的。与原并行回收器的不同在于采用了新的算法来处理老年代的垃圾回收。注意:实时上,并行压缩回收器将会取代并行回收器。

  1. 年轻代使用并行压缩回收器
        在并行压缩回收器中,年轻代垃圾回收使用的算法与并行回收器中使用的相同。

  2. 老年代使用并行压缩回收器
        在并行压缩回收器中,老年代和永久代是以一种暂停、并行的方式来移动压缩。回收器包括3各阶段。首先,每个代都会被分层固定大小的区块。在标记阶段,应用可以访问的存活对象集合被分割到不同的垃圾回收线程中,然后所有存活的类就会被并发标记。假如一个对象被认为是存活的,其所在区块的数据就会被更新,包括大小和对象的位置。
        汇总阶段要处理的是一个区块,而不是对象。因为先前回收的压缩,典型的情况是每个代左侧的部分包含较多存活对象,密度大。被如此高密度的区块覆盖的内存是不值得压缩的。汇总阶段首先要做的事情是检查区块的密度,从左侧的第一个块开始,直到达到某个临界点,在临界点右侧的被区块覆盖的内存是值得压缩的。临界点左侧的区域被认为是高密度的前缀,在那些区域没有对象需要移动。临界点的右侧需要被压缩,消除死的空间。汇总阶段会计算和存储每个压缩块存活数据块首字节的位置。注意:汇总阶段当前是串行执行的,并行化是可以的,但并不如在标记和压缩阶段那么重要。
        在压缩阶段,垃圾回收线程会使用汇总阶段的数据确定哪些区域需要被填充,然后各线程单并发向区域内拷贝数据。这会导致堆的一遍被高密度的堆积,另一边有一个大的空的内存块。

  3. 何时使用并行压缩回收器
        和并行回收器一样,并行压缩回收器对运行在多CPU的上应用是有好处的。另外,老年代的并行化操作降低了暂停时间,特别是对有暂停时间限制的应用,并行压缩回收器比并行回收器更合适。不过,并行压缩回收器对运行在大型共享内存机器上的应用不是很合适,因为没有某个应用可以独占若干个CPU超过一定的时段。在那些机器上,可以降低垃圾回收使用的线程数(通过设置-XX:ParallelGCThreads=n)或者选择不同垃圾回收器。

  4. 并行压缩回收器选择
        如果想使用并行压缩回收器,可以在声明命令行参数-XX:+UseParallelOldGC。

CMS回收器(Concurrent Mark-Sweep Collector)

对于许多应用,端到端的吞吐量并不如快速的响应时间来的重要。年轻代的回收通常不会导致长时间的暂停。但是,老年代的回收,尽管并不频繁,却会引起长时间的暂停,特别是使用大的堆时。为了解决这个问题,HotSpot虚拟机引入了一个称为CMS(Concurrent Mark-Sweep Collector,并发标记扫除回收器),以其低延时的特性而闻名。

  1. 年轻代使用CMS回收器
        CMS回收年轻代的方式同Parallel回收器相同。

  2. 老年代使用CMS回收器
        CMS在老年代的回收任务大部分是同应用的执行一起并发执行的。
    在CMS回收器回收循环的起始阶段,有一段短暂的暂停,称为初始化标记,用于确定应用直接可达的初始存活对象集。然后,在并发标记阶段,回收器标记所有的存活对象,这些对象是初始化对象集传递可达的。因为当标记是发生时,应用仍然在运行和更新引用,所以并不能保证所有存活的对象在标记结束时都能得到标记。为了处理这种情况,应用必须再次执行一个秒级的暂停,被称为再标记,再标记会通过重新访问所有在并发标记阶段更新的类来完成标记任务。因为再标记的暂停比起初始标记更大,因此通过多线程并发运行来提交其效率。
        在再标记结束时,所有对中存活对象是保证被标记,因此后续的并发扫除阶段会改造所有被确认的垃圾。如图7,说明使用串行Mark-Sweep-Compact回收器和CMS回收器在老年代的回收上的不同。


    图片 7.png

        因为一些任务,例如再标记阶段重新访问对象,会增加回收器的工作量,它的消耗也就会增加。对于大部分尝试减低暂停时间的回收器,这是一种典型的折中策略。
        CMS回收器是仅有的非压缩回收器,也就是在释放了死亡的对象所占用的空间后,并不会将存活对象移到老年代的一端,如图8。


    图片 8.png
        这节约了时间,但是因为释放的空间不是连续的,回收器不能再使用一个简单的指针来标记下一个空闲的可以用来给对象分配空间的内存位置。代替,需要采用一个空闲空间列表。未分配的空间通过一定数量的链表链接在一起,每一次为对象分配空间时,为了找到一个足够大能够容下对象的区域,必须搜索一个合适的链表。正因为如此,在老年代的内存分配上,相比使用空闲指针技术,代价更大。这也增加了年轻代回收上的额外消耗,因为大部分老年的分配是发生在年轻代回收中需要提升的对象上。
        CMS回收器另一个不利条件是要求更大相比其他回收器更大的堆空间。假如应用是被允许在在标记阶段运行时,它会继续分配内存,因此潜在地持续增加老年代。另外,回收器保证在标记阶段确认所有存活的对象,但是一些对象可能在标记阶段变为垃圾,它们并不会被回收直到下一次的老年代的回收。这种对象被称为漂浮垃圾。
        最后,由于没有压缩,碎片化或许会发生。为了处理碎片化,CMS会跟踪活跃的对象的大小,评估将来的需求,然后可能拆分或者合并空闲的空间来满足需求。
        并不像另一些回收器,CMS并不会在老年代变满时才开始老年代的回收。代替,它尝试更早开始回收工作,以便于它能够在老年代满之前完成回收。否则,CMS回收器将会返回到消耗更多时间的暂停mark-sweep-compact算法,像Parallel和Serial回收器所采用的。为了避免这种情况,CMS回收器将基于一些统计指标来启动,如之前回收的次数和老年代被占有的速度。如果老年代的占有率超过了初始设置的占有率,CMS回收器也会启动。初始化占有率设置,可以通过命令行参数-XX:CMSInitialingOccupancyFraction=n,n表示占有老年代的比例。默认是68。
        总的来说,相比Parallel回收器,CMS回收器有时会显著降低了老年代的暂停时间,代价是轻度的加长了年轻代的回收暂停时间,吞吐量的下降,更大的堆要求。
  3. 新增模式
        CMS可以运行在一种模式下,其并发阶段被递增地处理。这种模式意味着可以降低长时间并发阶段的影响,通过阶段性的暂停并发阶段,运行应用。回收任务被拆分为小的时间片,安排在两次年轻代回收之间。当应用需要在较少数量CPU的机器上运行并发回收器有较低的延迟时间,这种特性非常有用。更多信息,请查看“Tuning Garbage Collection with the 5.0 Java Virtual Machine”论文的第9章。

  4. 何时使用CMS
        如果应用需要更短的垃圾回收暂停时间,且更担负得起在应用运行时可以共享处理器资源,那么可以使用CMS回收器(因为并发的特性,CMS回收器在回收循环中会从应用抢占CPU的时间周期)。典型的情况,对于那些具有大量长时间存活数据(一个较大的老年代)以及运行在多CPU环境下的应用将会受益于CMS回收器。一个示例就是web服务器。CMS回收器可以被任何有低延时需求的应用考虑。对于在单处理上有适度大小的老年代的交互式应用,也会有一个好的表现。

  5. CMS的可选项
        如果你想使用CMS回收器,你必须声明命令行参数:-XX:+UseConcMarkSweepGC。如果你希望它运行在递增模式,可以通过-XX:+CMSIncrementalMode来使其生效。

第五章 工效学-自动化回收和行为转化

在J2SE 5.0的发行版中,垃圾回收器、堆大小、HotSpot虚拟机(client 还是server)的默认值是基于应用运行的操作系统和平台自动选择的。自动化的选择更好的匹配了不同类型应用的需求,而且比之前的版本要求更少的命令行参数。
    此外,一种新的切换回收器的方式也已经加入到并行垃圾回收器中。通过这种方法,用户说明希望的行为,垃圾回收器自动修改堆的尺寸,尝试获取期望的行为。根据平台依赖的默认选型和垃圾回收器切换的组合达到期望的行为被认为是工效学。工效学的目的就是提供更好的JVM执行能效,而需要更少的命令参数。
    自动化回收器、堆尺寸、虚拟机(运行模式)选择
    Server类型的机器通常定义为:

第六章 建议

上一章节描述的工效学所产生的自动化垃圾回收,虚拟机和堆尺寸选择,对大部分应用都是合理的。因此,初始的对垃圾回收器选择和配置的建议是什么都不做,即并不需要声明任何垃圾回收器的使用方法。让系统根据应用运行的平台和操作系统自动选择。然后测试系统。如果他的运行效果具有足够高的吞吐量,足够低的延时,那么它是可以接受的,你的工作就完成了。你不需要寻找故障或者修改垃圾回收器的选项。
    另一方面,如果你的应用在垃圾回收上面有一些运行能效问题,那么你能做的最容易的一件事件是思考一下默认的垃圾回收器对于你的应用和平台的特性是否合适。如果不,选择一个合适的回收器,看一下是否运行行为变的可接受。
    你可以通过使用第七章描述的工具来测试和分析运行能效。根据其结果,考虑是否要修改选择,例如堆尺寸、垃圾回收的行为。一些常见的选项会列举在第八章。注意:执行切换的最好方案是先测试,然后再切换。进行一些与你的代码运行相关的测试。同时,也要考虑到过优化,因为应用数据集,硬件等-甚至是垃圾回收器的执行-都会随着时间而改变。
    本章节将会提供一些信息关于如何选择垃圾回收器和说明堆尺寸。然后,提供一些切换Parallel回收器的建议,并对如何处理OOM给一些忠告。
    何时选择一个不同的垃圾回收器?
    第四章讲述了每一种回收器建议的使用场景。第五章描述Serial回收器或者Parallel回收器是如何被平台默认自动化选择的。如果你的应用或者运行环境的特性就是一个不同的回收器比默认的回收器更是想要的,那么可以通过如下命名进行明确的要求:

第七章 评估回收能效的工具

有多种诊断和监控工具可以用于评估垃圾回收的执行效果。本章节将对它们中的一些提供一个简要的概述。更多信息可以查看,第九章的“Tools and Troubleshooting”链接。
-XX:+PrintGCDetails命令行选项
    获取垃圾回收初始化信息的最简单方式之一是设置-XX:+PrintGCDetails命令行选项。每一次回收,都会输出每个分代在垃圾回收之前和之后,存活对象的多少,可用空间的多少,回收耗时。
-XX:+PrintGCTimeStamps命令行选项
    如果已经使用了-XX:+PrintGCDetail命令选项,那么会每一次输出的回收信息前加上时间信息。时间信息可以帮助你关联具有另外日志事件的回收日志。
jmap
    jmap是一个命令行工具包含在Solaris和Linux操作系统的(不包括window)的JDK发型版中。它会打印一些运行的JVM中与堆相关的统计信息或者核心文件。如果直接使用它不带用任何选项,它会打印加载的共享类列表,类似于Solaris的pmap工具集的输出。如果需要某些特殊信息,可以使用-heap、-histo、-permstat。
    heap选项用于获取包含垃圾回收器名称在内的信息,详细的算法(例如用于并行回收的线程数量),堆配置信息,堆使用率信息。
    histo选项用于获取获取堆中类的直方图统计。对于每个类,都会打印其在堆中的对象数量,哪些类消耗的内存大小,以及规格化的类名。直方图对于理解堆是如何使用的非常有用。
    配置永久代的大小对一些需要生产或者加载大量类的应用是非常重要的。如果一个应用加载的太多的类,OOM就会抛出。jmap的permstat选项可以用于获取永久代中类的统计信息。
jstat
    jstat工具集使用JVM的内嵌仪器提供一些运行应用的能效和资源消耗信息。诊断能效问题,特别是与堆尺寸和垃圾回收相关的问题,这个工具可能有用。它的一些选项可以打印关于垃圾回收和各代容量和消耗的统计信息。
HPROF: Heap Profiler
    HPROF是JDK 5.0中一个简单分析代理。它是一个动态链接库,通过JVM的工具接口联系JVM。它可以写出分析信息到文件或者通过以ASCII或者二进制的方式发送socket。这些信息可以紧接着通过一个分析工具进行处理。
    HPROF包括CPU使用率、堆分配统计、监控器竞争分析。另外,它可以输出完整的堆存储,报告所有JVM中监视器和线程的状态。HPROF对于分析能效,锁竞争,内存泄漏,及其他问题非常有用。
HAT: Heap Analysis Tool
    HAT可以帮助查找无意的类保留。这个属于是用于描述一个不再被需要,却依然保持存活,因为其引用仍然通过一些路径存在在一个存活的对象中。HAT提供一些遍历的工具浏览类使用HAPOF生产的堆快照中的拓扑结构。这个工具信息一些查询,包括“显示这个类所有的引用路径”。可以看第九章一个HAT说明文档的链接。

上一篇下一篇

猜你喜欢

热点阅读