JVM内存排查(1)

首先了解一下垃圾收集工作原理

        JVM使用一种称为跟踪收集器的垃圾收集器,它通过stop-the-world来操作垃圾收集,标记所有根对象(由主线程直接引用的对象),并遵循它们的引用,标记它沿途看到的每个对象。如果发现某些对象没有任何引用,就标记为垃圾对象,对其进行收集(如下图)。

  • Young Generation -年轻代。这里只会出发最小垃圾回收(Minor GC ),所以代价最小,时间最快。它有两个子代
    • Eden Space -对象从这里开始。大多数物体都是在Eden Space中创造和销毁的。在这里,GC执行Minor GCs,这是优化的垃圾收集。执行Minor GC时,对仍然需要的对象的任何引用都将迁移到其中一个survivors空间(S0或S1)。
    • Survivor Space (S0 and S1)-幸存Eden Space的对象最终来到这里。其中有两个,在任何给定时间只有一个正在使用(除非我们有严重的内存泄漏)。一个被指定为空,另一个被指定为活动,与每个GC循环交替。
  • Tenured Generation -也被称为老年代(图2中的旧空间),这个空间容纳存活较长的对象,使用寿命更长(如果它们活得足够长,则从Survivor空间移过来)。填充此空间时,GC会执行完整GC,时间较长,代价较大,这会在性能方面降低成本。如果此空间无限制地增长,则JVM将抛出OutOfMemoryError - Java堆空间。
  • Permanent Generation -作为与终身代密切相关的第三代,永久代是特殊的,因为它保存虚拟机所需的数据,以描述在Java语言级别上没有等价的对象。例如,描述类和方法的对象存储在永久代中。此处的内存不会进行垃圾回收

比较典型的内存泄露demo

package com.post.memory.leak;

import java.util.Map;

public class MemLeak {
    public final String key;

    public MemLeak(String key) {
        this.key =key;
    }

    public static void main(String args[]) {
        try {
            Map map = System.getProperties();
            for(;;) {
                map.put(new MemLeak("key"), "value");
            }
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}

此处如果我们已经正确实现了equals()和hashcode()方法,那么即使使用无限循环,代码也能正常运行,因为在HashMap中一直都只有一个元素。但是因为没有正确实现hash和equal方法,hashMap会不断的把“value”放入其中,这些value一直都得不到回收,从而导致内存泄漏。

 使用Java VisualVM发现内存泄漏

         仅仅30秒之后,老年代几乎已满,表明即使使用Full GC,老年代也在不断增长,这是内存泄漏的明显迹象。(原因待考,二者好像没有必然的关联)。

检测此泄漏原因的一种方法如下图所示(单击放大),使用带有heapdump的Java VisualVM生成。在这里,我们看到50%的Hashtable $ Entry对象在堆中,而第二行指向MemLeak类。因此,内存泄漏是由MemLeak类中使用的哈希表引起的。

确定频繁Full GC现象

  • jps命令格式为:

jps [ options ] [ hostid ]
使用命令如下:
使用jps:jps -l

使用ps:ps aux | grep tomat 找到你需要监控的ID(假设为20954),再利用“虚拟机统计信息监视工具:jstat”监视虚拟机各种运行状态信息。

jstat命令格式为:
jstat [ option vmid [interval[s|ms] [count]] ]
使用命令如下:
jstat -gcutil 20954 1000
意思是每1000毫秒查询一次,一直查。gcutil的意思是已使用空间站总空间的百分比。

结果如下图:

 

jstat执行结果

查询结果表明:

  • 新生代Eden区(E,表示Eden)使用了28.30%(最后)的空间,
  • 两个Survivor区(S0、S1,表示Survivor0、Survivor1)分别是0和8.93%,
  • 老年代(O,表示Old)使用了87.33%。
  • 共发生Minor GC(YGC,表示Young GC)101次,总耗时1.961秒,
  • 发生Full GC(FGC,表示Full GC)7次,Full GC总耗时3.022秒,总的耗时(GCT,表示GC Time)为4.983秒。

 找出导致频繁Full GC的原因

jmap命令格式:
jmap [ option ] vmid
使用命令如下:
jmap -histo:live 20954

可以看出HashTable中的元素有5000多万,占用内存大约1.5G的样子。这肯定不正常。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值