首先了解一下垃圾收集工作原理
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类中使用的哈希表引起的。
- 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秒。
jmap命令格式:jmap [ option ] vmid
使用命令如下:jmap -histo:live 20954
可以看出HashTable中的元素有5000多万,占用内存大约1.5G的样子。这肯定不正常。