JVM垃圾回收

Java 堆是垃圾收集器管理的主要区域,因此也被称作GC 堆(Garbage Collected Heap)。从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以 Java 堆还可以细分为:新生代和老年代:再细致一点有:Eden 空间、From Survivor、To Survivor 空间等。进一步划分的目的是更好地回收内存,或者更快地分配内存。

在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。

Minor Gc和Full GC

新生代 GC(Minor GC):从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC,因为 Java 对象大多都具备朝生夕灭的特性,所以 Minor GC 非常频繁,一般回收速度也比较快。

老年代 GC(Major GC / Full GC):指发生在老年代的 GC,出现了 Major GC,经常会伴随至少一次的 Minor GC(但非绝对的,ParallelScavenge 收集器的收集策略里就有直接进行 Major GC 的策略选择过程) 。MajorGC 的速度一般会比 Minor GC 慢 10倍以上。

Major GC 是清理永久代。 Full GC 是清理整个堆空间—包括年轻代和永久代。

Minor Gc触发机制

当年轻代满时就会触发Minor GC,这里的年轻代满指的是Eden代满,Survivor满不会引发GC。

Full GC触发机制

1.调用System.gc时,系统建议执行Full GC,但是不必然执行

2.老年代空间不足

3.方法区空间不足

4.通过Minor GC后进入老年代的平均大小大于老年代的可用内存

5.由Eden区、survivor space1(From Space)区向survivor space2(To Space)区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

6.永久代满时也会触发,会导致Class、Method元信息的卸载。

GC判断对象存活算法

1.引用计数法:给对象增加一个引用计数器,有地方引用就加1,引用失效就减1.为0就是不可再被使用的。问题:相互循环引用的对象是无法被识别出来并且被回收的。

2.根地址法:从一个根出发,搜索所有的可达对象,这样剩下的那些对象就是需要被回收的。

引用

1.强引用:如果一个对象具有强引用,那就类似于必不可少的生活用品,垃圾回收器绝不会回收它。当内存空间不足,Java 虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。

2.软引用:如果一个对象只具有软引用,那就类似于可有可无的生活用品。如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。

3.弱引用:如果一个对象只具有弱引用,那就类似于可有可无的生活用品。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。

4.虚引用:虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。

虚引用主要用来跟踪对象被垃圾回收的活动。 虚引用必须和引用队列(ReferenceQueue)联合使用。

判断无用类:

1.该类的所有实例被回收,堆中不存在该类的任何实例。

2.加载该类的ClassLoader已被回收

3.该类对应的Java.lang.Class对象没有在任何地方被引用。无法在任何地方通过反射访问该类的方法。

垃圾收集算法:

标记-清楚算法:标记清楚两个阶段,首先标记出所有需要回收的对象,完成后统一回收所有被标记的对象,问题:(1)效率问题(2)产生空间碎片

复制算法:将内存分为大小相同的两块,每次使用其中一块。当这一块内存使用完成后,就将还存活的对象复制到另一块去,再把使用的空间清除。解决效率问题,只回收一半内存。

标记-整理算法:标记过程和标记-清除算法一样,后续步骤不是对可回收对象回收,而是让所有存活的对象向一端移动,清除端边界外的内存。

分代收集算法:根据对象存活周期的不同将内存分为几块,一般将Java堆分为新生代和老年代。根据各个年代的特点选择合适的算法。如:新生代,每次收集都有大量对象死去,选择复制算法,而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进行垃圾收集。

垃圾收集器:

serial收集器:串行收集器,单线程。工作时别的线程都得停止。新生代:复制算法,老年代:标记-整理。

ParNew收集器:serial收集器的多线程版本,除了垃圾收集器多线程外,其余行为和serial一样。

Parallel scavenge:Parallel Scavenge 收集器也是使用复制算法的多线程收集器。关注点是吞吐量。新生代:复制算法,老年代:标记-整理。

CMS收集器:并发收集器(垃圾收集线程和用户线程同时工作),用标记清算法实现。以获取最短回收停顿时间为目标。

初始标记:暂停所有其他线程,记录直接与root相连的对象,

并发标记:同时开启用户和GC线程,用一个闭包结构去记录可达对象,但在这个阶段结束,这个闭包结构并不能保证包含当前所有的可达对象。因为用户线程可能会不断地更新引用域,所以 GC 线程无法保证可达性分析的实时性。所以这个算法里会跟踪记录这些发生引用更新的地方。

重新标记:修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短

并发清除:开启用户线程,同时GC线程开始清除。

优点:并发收集、低停顿。 缺点:对CPU资源敏感,无法处理浮动垃圾,它使用的回收算法-“标记-清除”算法会导致收集结束时会有大量空间碎片产生。

G1收集器:面向服务器的垃圾收集器,针对配备多个处理器及大容量内存的机器。以极高概率满足 GC 停顿时间要求的同时,还具备高吞吐量性能特征.

特点:

并发并行:G1 能充分利用 CPU、多核环境下的硬件优势,使用多个 CPU(CPU 或者 CPU 核心)来缩短 Stop-The-World 停顿时间。部分其他收集器原本需要停顿 Java 线程执行的 GC 动作,G1 收集器仍然可以通过并发的方式让 java 程序继续执行。

6分代收集:G1不需要其他收集器配合就能独立管理整个GC堆,保留了分代的概念。

空间整合:与 CMS 的“标记–清理”算法不同,G1 从整体来看是基于“标记整理”算法实现的收集器;从局部上来看是基于“复制”算法实现的。

可预测的停顿:G1 除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为 M 毫秒的时间片段内

OMM排查:


1.java.lang.OutOfMemoryError: Java heap space ——>java堆内存溢出,此种情况最常见,一般由于内存泄露或者堆的大小设置不当引起。对于内存泄露,需要通过内存监控软件查找程序中的泄露代码,而堆大小可以通过虚拟机参数-Xms,-Xmx等修改。

2.
java.lang.OutOfMemoryError: PermGen space ——>java永久代溢出,即方法区溢出了,一般出现于大量Class或者jsp页面,或者采用cglib等反射机制的情况,因为上述情况会产生大量的Class信息存储于方法区。此种情况可以通过更改方法区的大小来解决,使用类似-XX😛ermSize=64m -XX:MaxPermSize=256m的形式修改。另外,过多的常量尤其是字符串也会导致方法区溢出。

3.
java.lang.StackOverflowError ——> 不会抛OOM error,但也是比较常见的Java内存溢出。JAVA虚拟机栈溢出,一般是由于程序中存在死循环或者深度递归调用造成的,栈大小设置太小也会出现此种溢出。可以通过虚拟机参数-Xss来设置栈的大小。

要dump堆的内存镜像,可以采用如下两种方式:

?设置JVM参数-XX:+
HeapDumpOnOutOfMemoryError,设定当发生OOM时自动dump出堆信息。不过该方法需要JDK5以上版本。

?使用JDK自带的jmap命令。”jmap -dump:format=b,file=heap.bin ” 其中pid可以通过jps获取。

dump堆内存信息后,需要对dump出的文件进行分析,从而找到OOM的原因。常用的工具有:

?mat: eclipse memory analyzer, 基于eclipse RCP的内存分析工具。

?jhat:JDK自带的java heap analyze tool,可以将堆中的对象以html的形式显示出来,包括对象的数量,大小等等,并支持对象查询语言OQL,分析相关的应用后,可以通过http://localhost:7000来访问分析结果。不推荐使用,因为在实际的排查过程中,一般是先在生产环境 dump出文件来,然后拉到自己的开发机器上分析,所以,不如采用高级的分析工具比如前面的mat来的高效。

声明:本站部分文章内容及图片转载于互联 、内容不代表本站观点,如有内容涉及侵权,请您立即联系本站处理,非常感谢!

(0)
上一篇 2022年1月18日
下一篇 2022年1月18日

相关推荐