深入JVM内核原理-9.Java堆分析

1.概要

  • 内存溢出(OOM)的原因
  • MAT使用基础
    • 浅堆(Shallow Heap)与深堆(Retained Heap)
    • 显示入引用(incoming)和出引用(outgoing)
    • 支配树
  • 使用Visual VM分析堆
  • Tomcat OOM分析案例

2.内存溢出(OOM)的原因

  • 在JVM中,有哪些内存区间
    • 永久区
    • 线程栈
    • 直接内存
  • 堆溢出
    public static void main(String[] args) {
        ArrayList 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.(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可以及时过期,并回收

你可能感兴趣的:(深入JVM内核原理-9.Java堆分析)