JVM垃圾回收机制

JVM垃圾回收机制

JVM的组成

程序计数器

执行class文件到达哪一行了,线程私有的,不会发生内存泄漏。

内存管理最大的一部分,当程序里new出一个对象,或者声明一个数组时,都会在堆内存中申请出一块空间。

分为新生代、年老代、永久代。

发生异常时是因为往里面添加的对象太多。

虚拟机栈

线程私有,与线程共存。

发生异常时是因为方法进去后无法出栈。

本地方法栈

直接跟操作系统打交道,不加载Java方法,与虚拟机栈互相调用。

方法区

内存分配与回收策略

判断对象已死的算法

引用计数器算法(Java中现在不使用)

可达性分析算法(目前在使用)

再谈引用

垃圾回收算法

标记-清除算法

标记-整理算法

复制算法

分代垃圾回收

垃圾收集器

jdk1.7 默认垃圾收集器Parallel Scavenge(新生代)+ Serial Old(老年代)

jdk1.8 默认垃圾收集器Parallel Scavenge(新生代)+ Serial Old(老年代)

jdk1.9 默认垃圾收集器G1

Serial收集器

Serial:串行(-XX:+UseSerialGC)>为单线程环境设计,且使用一个线程回收垃圾,会暂停所有的用户线程,不适合服务器环境(例如:用户用餐,餐厅让用户出去,要叫一个清洁工打扫,打扫完再回来吃)

PerNew收集器(Serial收集器的多线程版本)

Parallel Scavenge收集器

Parallel:并行(-XX:+UseParallelGC)>多个并行垃圾收集线程工作,此时用户线程是暂停的,适用于科学计算、大数据处理首台处理等若交互环境(例如:用户用餐,餐厅让用户出去,要叫多个清洁工打扫,打扫完再回来吃)

Serial Old收集器

Serial收集器的老年代版本。

Parallel Old收集器

Parallel Scavenge收集器老年代版本。

CMS收集器(边污染边清除)

CMS:(-XX:UseConcMarkSweepGC)>用户线程和垃圾收集线程同时执行(并不一定是并行,可能交替执行),不需要停顿用户线程
,适用对响应时间有要求的场景(例如:用户用餐,餐厅叫多个清洁工进来打扫,边吃边打扫)

G1收集器

G1:(garbage first)(-XX:UseG1GC)>G1垃圾回收器将堆内存分割成不通的区域然后并发的对其进行垃圾回收 java11默认GC回收器是ZGC

常见参数分配

GC文字描述

我们都知道在Java虚拟机中进行垃圾回收的场所有两个,一个是堆,一个是方法区。

在堆中存储了Java程序运行时的所有对象信息,而垃圾回收其实就是对那些“死亡的”对象进行其所侵占的内存的释放,让后续对象再能分配到内存,从而完成程序运行的需要。关于何种对象为死亡对象,在下一部分将做详细介绍。

Java虚拟机将堆内存进行了“分块处理”,从广义上讲,在堆中进行垃圾回收分为新生代(Young Generation)和老生代(Old Generation);

从细微之处来看,为了提高Java虚拟机进行垃圾回收的效率,又将新生代分成了三个独立的区域(这里的独立区域只是一个相对的概念,并不是说分成三个区域以后就不再互相联合工作了),

分别为:Eden区(Eden Region)、From Survivor区(Form Survivor Region)以及To Survivor(To Survivor Region),而Eden区分配的内存较大,其他两个区较小,每次使用Eden和其中一块Survivor。

Java虚拟机在进行垃圾回收时,将Eden和Survivor中还存活着的对象进行一次性地复制到另一块Survivor空间上,直到其两个区域中对象被回收完成,当Survivor空间不够用时,需要依赖其他老年代的内存进行分配担保。

当另外一块Survivor中没有足够的空间存放上一次新生代收集下来的存活对象时,这些对象将直接通过分配担保机制进入老生代,在老生代中不仅存放着这一种类型的对象,还存放着大对象(需要很多连续的内存的对象),当Java程序运行时,如果遇到大对象将会被直接存放到老生代中,长期存活的对象也会直接进入老年代。

如果老生代的空间也被占满,当来自新生代的对象再次请求进入老生代时就会报OutOfMemory异常。新生代中的垃圾回收频率高,且回收的速度也较快。

就GC回收机制而言,JVM内存模型中的方法区更被人们倾向的称为永久代(Perm Generation),保存在永久代中的对象一般不会被回收。

其永久代进行垃圾回收的频率就较低,速度也较慢。永久代的垃圾收集主要回收废弃常量和无用类。以String常量abc为例,当我们声明了此常量,那么它就会被放到运行时常量池中,如果在常量池中没有任何对象对abc进行引用,那么abc这个常量就算是废弃常量而被回收;判断一个类是否“无用”,则需同时满足三个条件:

(1)、该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例;

(2)、加载该类的ClassLoader已经被回收

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

虚拟机可以对满足上述3个条件的无用类进行回收,这里说的是可以回收而不是必然回收。

大多数情况下,对象在新生代Eden区中分配,当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC;

同理,当老年代中没有足够的内存空间来存放对象时,虚拟机会发起一次Major GC/Full GC。

只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小就会进行Minor GC,否则将进行Full CG。

虚拟机通过一个对象年龄计数器来判定哪些对象放在新生代,哪些对象应该放在老生代。

如果对象在Eden出生并经过一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并将该对象的年龄设为1。对象每在Survivor中熬过一次Minor GC,年龄就增加1岁,当他的年龄增加到最大值15时,就将会被晋升到老年代中。

虚拟机并不是永远地要求对象的年龄必须达到MaxTenuringThreshold才能晋升到老年代,如果在Survivor空间中所有相同年龄的对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需等到MaxTenuringThreshold中要求的年龄。

参考资料

原文地址:https://www.cnblogs.com/renxiuxing/p/14978626.html