1.概要
- 内存溢出(OOM)的原因
- MAT使用基础
- 浅堆(Shallow Heap)与深堆(Retained Heap)
- 显示入引用(incoming)和出引用(outgoing)
- 支配树
- 使用Visual VM分析堆
- Tomcat OOM分析案例
2.内存溢出(OOM)的原因
- 在JVM中,有哪些内存区间
- 堆
- 永久区
- 线程栈
- 直接内存
- 堆溢出
public static void main(String[] args) { ArrayList<byte[]> list = new ArrayList<>(); for (int i = 0; i < 1024*10; i++) { list.add(new byte[1024*1024]); } } Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at GC.Test3.main(Test3.java:12)
- 解决方法:增大堆空间,及时释放内存
- 永久区
- -XX:MetaspaceSize=1m -XX:MaxMetaspaceSize=1m -XX:+PrintGCDetails
public class Test3 { public static void main(String[] args) { for (int i = 0; i < 1000000; i++) { CglibBean bean = new CglibBean("geym.jvm.ch3.perm.bean"+i,new HashMap()); } } } class CglibBean { /** * 实体Object */ public Object object = null; /** * 属性map */ public BeanMap beanMap = null; public CglibBean() { super(); } @SuppressWarnings("unchecked") public CglibBean(String name, Map propertyMap) { this.object = generateBean(name, propertyMap); this.beanMap = BeanMap.create(this.object); } /** * 给bean属性赋值 * * @param property 属性名 * @param value 值 */ public void setValue(String property, Object value) { beanMap.put(property, value); } /** * 通过属性名得到属性值 * * @param property 属性名 * @return 值 */ public Object getValue(String property) { return beanMap.get(property); } /** * 得到该实体bean对象 * * @return */ public Object getObject() { return this.object; } @SuppressWarnings("unchecked") private Object generateBean(String name, Map propertyMap) { BeanGenerator generator = new BeanGenerator(); Set keySet = propertyMap.keySet(); for (Iterator i = keySet.iterator(); i.hasNext(); ) { String key = name + (String) i.next(); generator.addProperty(key, (Class) propertyMap.get(key)); } return generator.create(); } } Error occurred during initialization of VM OutOfMemoryError: Metaspace
- 解决方法:增大永久区,允许Class回收
- Java栈溢出
- 这里的栈溢出指,在创建线程的时候,需要为线程分配栈空间,这个栈空间是向操作系统请求的,如果操作系统无法给出足够的空间,就会抛出OOM
设置堆空间 -Xmx1m -Xss1m
public static void main(String[] args) { for (int i = 0; i < 10000; i++) { new Thread(new SleepThread(), "Thread" + i).start(); System.out.println("Thread" + i + " created"); } } public static class SleepThread implements Runnable{ @Override public void run() { try { Thread.sleep(100000000); } catch (InterruptedException e) { e.printStackTrace(); } } } Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at GC.Test3.main(Test3.java:11)
- 解决方法:减少堆内存,减少线程栈大小
- 直接内存溢出
- ByteBuffer.allocateDirect()无法从操作系统获得足够的空间
-Xmx1m -XX:+PrintGCDetails
for (int i = 0; i < 1024; i++) { ByteBuffer.allocateDirect(1024*1024*10); System.out.println(i); System.gc(); } [GC (Allocation Failure) [PSYoungGen: 1024K->488K(1536K)] 1024K->624K(5632K), 0.0035865 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (System.gc()) [PSYoungGen: 1393K->504K(1536K)] 1529K->724K(5632K), 0.0007902 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (System.gc()) [PSYoungGen: 504K->0K(1536K)] [ParOldGen: 220K->654K(4096K)] 724K->654K(5632K), [Metaspace: 3408K->3408K(1056768K)], 0.0077157 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory at java.nio.Bits.reserveMemory(Bits.java:694) at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123) at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311) at GC.Test3.main(Test3.java:12) Heap PSYoungGen total 1536K, used 133K [0x00000000ffe00000, 0x0000000100000000, 0x0000000100000000) eden space 1024K, 13% used [0x00000000ffe00000,0x00000000ffe214d8,0x00000000fff00000) from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000) to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000) ParOldGen total 4096K, used 654K [0x00000000ffa00000, 0x00000000ffe00000, 0x00000000ffe00000) object space 4096K, 15% used [0x00000000ffa00000,0x00000000ffaa39a0,0x00000000ffe00000) Metaspace used 3497K, capacity 4496K, committed 4864K, reserved 1056768K class space used 384K, capacity 388K, committed 512K, reserved 1048576K
- 解决方法:减少堆内存,有意触发GC
3.MAT使用基础
- 简介
- Memory Analyzer(MAT)
- 基于Eclipse的软件
- http://www.eclipse.org/mat/
- 浅堆
- 一个对象结构所占用的内存大小
- 3个int类型以及一个引用类型合计占用内存3*4 + 4=16个字节。再加上对象头的8个对象占用的空间,即浅堆的大小是16+8=24字节
- 对象大小按照8字节对齐
- 浅堆大小和对象的内容无关,只和对象的结构有关
- 深堆
- 一个对象被GC回收后,可以真实释放的内存大小
- 只能通过对象访问到的(直接或间接)所有对象的浅堆之和(支配树)
4.使用Visual VM分析堆
- java自带的多功能分析工具,可以用来分析堆Dump
5.Tomcat OOM分析案例
- Tomcat OOM
- Tomcat在接受大量请求时发生OOM,获取堆Dump文件,进行分析。
- 使用MAT打开堆
- 分析目的:
- 找出OOM的原因
- 推测系统OOM时的状态
- 给出解决这个OOM的方法
- 解决方法:
- OOM由于保存session过多引起,可以考虑增加堆大小
- 如果应用允许,缩短session的过期时间,使得session可以及时过期,并回收
网友评论