JVM之OOM分析

一.OOM疑难杂症

GC Roots

  • 对象引用关系对象引用关系

  • GC Roots是一组必须活跃的引用,程序通过直接、间接引用能够访问到的潜在使用的对象

     包含:
    1.java线程,当前所有正在被调用方法的引用类型参数、局部变量、临时值等,即与栈帧相关的各种引用
    2.所有当前被加载的java类
    3.运行时常量池的引用类型(String或class类型)
    4.Jvm内部数据结构引用,如sun.jvm.hotspot.memory.Universe等
    5.用于同步的监控对象,如调用对象的wait()方法
    6.JNI handles,包括global handles ,local handles
    
  • GC Roots分类

    这里说的是活跃的引用,而不是对象,对象时不能作为GC Roots。
    GC过程是找出所有活对象,并把其余(堆)空间认定为”无用”;而不是找出所有死掉对象,并回收其空间,所以即使jvm堆非常大,基于tracing的GC方法,回收速度也非常块。
    具体GC Roots分类有:
    1.活动现场相关的各种引用
    2.类的静态变量的引用
    3.JNI引用
    

引用级别

引用级别分类

  • 强引用Strong references

    1.特点:
    (a).当内存空间不足,系统撑不住了,JVM就会抛出OutOfMemoryError错误
    (b).即使程序异常终止,也不会对该类对象回收(无论如何也不会回收)
    (c).该引用属于最普通,最强硬的一种存在,只有在和GC Roots断绝关系时,才会被消灭掉
    2.实现方式: Object obj = new Object();
    3.存在问题:内存泄漏。
    如下代码:如果系统大量用户(存储在User对象)访问,需要记录User访问时间。User对象无该时间字段,所以用Map结构存放。我们希望用完了某个User对象后,它能够被回收掉。但是它被userVisitMap引用,无其他手段删除掉它,这时会发生内存泄漏。
    该情况也会发生在未设定上限的缓存系统,由于设置不正确引用方式,加上不正确容量,易造成OOM
    
    Static Map<User,Long> userVisitMap = new HashMap<>();
    userVisitMap.put(user,time);
    
  • 软引用 Soft reference

    1.特定:
    (a)内存不足时,软引用对象被回收
    (b)回收后仍然无足够内存,会抛出内存溢出异常
    (c)改特效非常实用在缓存技术上,网页缓存
    (d)与引用队列结合使用,根据对象进入队列顺序从先到后进行删除
    2.实现方式
    Object obj = new Object();
    //通过泛型来定义
    SoftReference<Object> softRef = new SoftReference(object);
    
    3.注意事项:谨慎设置
    每个MB堆空间中SoftReference的存活时间默认为1s(1000ms): -XX:SoftRefLRUPolicyMSPerMB=<N>
    
  • 弱引用

     1.特点:
    (a)较软引用,更加无用一些,声明周期更短
    (b)用WeakReference来表示
     2.实现
    Object obj = new Object();
    WeakReference<Object> softRef = new WeakReference(object);
    
    
  • 虚引用

    1.特点
    (a):形同虚设的引用,应用不太多
    (b)必须和引用队列联合使用
    (c)如果一个对象仅持有,那么它和无任何引用一样,任何时候都会被回收
    (d)主要用来跟踪对象被垃圾回收的活动,当GC 回收一个对象时,若发现还有虚引用,就会在回收对象之前,则将这个虚引用加入与之关联的引用队列中
    2.实现
    //虚引用的get()方法,总是被返回null
     	 Object obj = new Object();
    ReferenceQueue queue = new ReferenceQueue();
    //虚引用,必须与一个引用队列关联
    PhantomReference pr = new PhantomReference(object,queue);
    3.例子
    Private static void startMonitoring(ReferenceQueue<MyObject> referenceQueue,Reference<MyObject> ref ){
    
    ExecutorService ex = Executors.newSingleThreadExecutor():
    ex.execute(()->{
    While(referenceQueue.poll() != ref){
    //don’t hang forever
    If(finishFlag){
    Break;
    }
    }
    System.out.print(“-- ref gc’ed -- ”);
    });
    ex.shutdown();
    }
    4.扩展
    基于虚引用,更加优雅的方式是,Java9 新加入cleaner,用来替代Object类的finalizer方法,进行对象回收后的处理工作。
    

典型OOM场景

  • 发生位置
    1.线程私有:Java虚拟机栈,本地方法栈
    2.线程共享:方法区,直接内存,堆外内存
    
  • 触发原因
    1.内存设置过小,需要调整堆空间大小(-Xms,-Xmx)
    2.错误引用方式,发生内存泄漏。没有及时切断与GC Roots的关系。如线程池的线程,在复用的情况下忘记清理ThreadLocal的内容
    3.接口没有进行范围校验,外部传参超出范围。如数据库查询每页条数,查询数据过大
    4.对堆外内存的无限制使用。一旦发生更加严重,会造成草系统内存耗尽而夯住。
    
  • 解决
    及时释放对象引用(适时手动清理),避免对象到处引用。如避免局部变量被一个静态集合所引用,造成无法进行对象释放,不能被GC清理,如A类的局部变量local数组被B类的成员变量 静态map引用,无法释放
    Class A{
    public  void test(String a){
    int[] local = new int[];
    }
    }
    

小结

 GC Roots专业叫作 可达性分析法。另外还有引用计数法的方式。判断对象问题,常被提及
因为有循环依赖、间接引用的硬伤,jvm都不采用。实现方式是对象头维护一个counter计数器,被引用一次+1,引用失效-1,计数器为0时为无效,可以进行回收。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值