一.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时为无效,可以进行回收。