简述JVM垃圾回收机制

简述JVM垃圾回收机制1.Java中的四种引用类型在Java中,对于引用最基本的解释就是:如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用(有点指针的意味)。后来Java还将引用划分为了4种,根据被GC回收的时机可以分为:强引用(StrongReference)、软引用(SoftReference)、弱引用(WeakReference)、虚引用(Phantor

大家好,又见面了,我是你们的朋友全栈君。

1. Java中的四种引用类型

在Java中,对于引用最基本的解释就是:如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用(有点指针的意味)。后来Java还将引用划分为了4种,根据被GC回收的时机可以分为:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantorm Reference)。这4种引用的强度依次渐弱。

1.1 强引用

Object o = new  Object();

这里的o就是一个强引用,也是我们用得最多的引用,在实例化类的时候经常会用到。遇到这类引用,GC(垃圾回收器)是绝对不会回收它的。当遇到内存不足的情况,JVM会抛出OOM异常。所以,在不使用这类对象的时候要注意释放它,以便让系统回收。

1.2 软引用

SoftReference<String> s = new SoftReference<>(new String("Hello"));
System.out.println(w.get());

此时会输出
Hello

这里的s就是一个软引用,它是用来描述一些有用但非必需的对象,当系统内存出现不足的时候,会立即把这些对象进行回收。
软引用可以用来实现内存的缓存,如图片缓存。

1.3 弱引用

WeakReference<String> w = new WeakReference<>(new String("Hello"));
System.out.println(w.get());
System.gc();
System.out.println(w.get());

此时会输出:

Hello
null

弱引用的强度比软引用低一些,一旦GC开始执行,便会对这类对象进行回收。也就是说无论内存足够与否,弱引用对象都只能生存到下一次GC之前。

1.4 虚引用

ReferenceQueue<String> queue = new ReferenceQueue<>();
PhantomReference<String> pr = new PhantomReference<>(new String("Hello"), queue);
System.out.println(pr.get());

此时会输出:

null

虚引用也称为幽灵引用,它的强度是最低的,一个对象是否有虚引用的存在,完全不会对它的生存时间构成影响,当然也无法通过虚引用获取一个类的实例,GC任何时候都有可能会回收此对象。它唯一的作用就是就是用于追踪,让我们能够在这个对象被回收的时候收到一个通知。

2.如何判断对象需要被回收

2.1 引用计数法

比较尴尬的是以前刚上Java课的时候,我去问老师JVM是如何确定需要被回收的对象的时候,老师就给我讲了引用计数法,但后来随着学习的深入,我发现主流的JVM都没有采用这一方法,原因是它有很大的一个缺陷,就是难以解决对象之间相互引用问题。
所谓引用计数法,就是在对象内部会有一个引用计数器,一旦某个地方引用它时,计数器就加1,表面上看这是一个效率非常高的方式,但是如果遇到如下情形时,采用了这种方式判断对象是否存活的GC就难以发挥作用了。

public static void main(String[] args) {
    A a = new A();
    B b = new B();
    //a,b循环引用对方
    a.intance = b; //b的引用计数加1
    b.intance = a; //a的引用计数加1
    //虽然a和b都被设置成null了,但它们的引用计数仍然是1,不会被GC回收 
    a = null;
    b = null;
    System.gc();
}

2.2 可达性分析法

相比之下,可达性分析算法就要靠谱得多。所谓可达性分析就是通过一系列被称为“GC Roots”的点作为起始点,从这些节点开始向下搜索,搜索的路径称为引用链,当一个对象到GC Roots不可达的时候,则证明此对象是可回收的。
GC Root
上图中,从GC Root到ObjectC、ObjectE、ObjectF都不可达,所以这三个对象就是会被GC回收的对象。

3.垃圾回收算法

3.1 标记清除法

标记清楚法有两个过程,一是标记过程,即判定需要回收的对象的过程,通过可达性分析便可分析出来。二是清除阶段,这个阶段就是对标记了的对象进行回收。
标记清除法
这种回收算法非常简单粗暴,也很好理解,但是暴露出来问题还是比较大的,需要优化的地方还有很多。一是标记和清除的效率都不是很高。二是执行完GC后会造成大量的内存碎片,如果以后分配大内存对象的的时候无法找到足够大的连续内存,就会频繁触发GC。

3.2 复制算法

为了解决内存碎片问题,复制算法出现了,它将可用内存平均划分为两块,每次只使用其中一块。当这一块用完了,就将还存活的对象复制到另一块,然后再把已使用的内存空间一次清理掉。这样每次GC的时候都直接回收半个内存空间的大小,不必考虑碎片问题。但这种方法带来的代价也不小,牺牲了一半的存储空间。
复制算法

3.3 标记整理法

如果在对象存活率较高的时候采用复制算法的话,复制的操作就会有很多,对程序的运行效率也会产生一定的影响,此时就应该考虑标记整理法。标记整理法的执行过成分为3步:1.用与“标记清除法”一样的操作标记存活的对象;2.将被标记的对象全部移动到内存的某一端;3.清除边界以外部分的内存。
标记整理法

3.4 分代回收法

在分代回收算法中,会根据对象的存活周期,将内存划分为几块,一般是新生代(Young Generation)、老年代(Old Generation)、永久代(Permanent Generation)。这样就可以根据不同内存区域的特点执行采用不同的回收算法。像新生代这种经常有大批对象死去的区域,就适合用复制算法。而对于老年代这种对象生存周期较长和永久代这种内存存活率较高,又没有其他担保空间的地方就用标记清除法或标记整理法就行了。

3.4.1 新生代

对于新生代,往往会分为三个区:一个Eden区和两个Survivor区(图中是S0和S1,其实这两个区域没有任何区别,只是取个名字方便叙述)。这样划分后结合复制算法对垃圾进行回收效果和效率都会比较高。具体的工作流程如下:
新生代的分区
程序中大部分对象都是在新生代的Eden区域中,当GC来临时,会遵循如下机制:
1.当Eden区域触发GC的时候,将Eden中还存活的对象复制到S0中,再清空Eden区。
2.然后当S0触发GC时候,将S0和Eden中还存活的对象复制到S1中,再清空S0和Eden区。
3.当S1也触发GC的时候,就将此区域内的从S0复制过来并且GC后还存活的对象复制到老年代,再清空S1。
值得注意的是:GC发生的时机是根据剩余内存的大小和具体的算法来决定的,不一定是当内存存满了才触发一次GC。并且这里的GC指的是Minor GC,也就是用一些快速回收算法实现的高效率GC,这种方式只会对新生代进行回收。

3.4.2 老年代

老年代中则存放着新生代复制过来的对象,这些一般都是经历过多次GC存活下来的对象,所以这里的对象生存的时间都比较长。当老年代存满了时候,会调用Full GC也就是对整个堆(包括新生代、老年代、永久代)进行回收,这样的回收更彻底,但是时间也消耗更多。

3.4.3 永久代

永久代一般用来存放一些静态变量,静态方法等,这里一般发生GC的概率比较小,但是一旦存满,也是会直接触发Full GC的。

4.内存分配策略

JVM的一大优势是解决的内存方面的两个重要的问题:自动给对象分配内存 和 自动回收分配给对象的问题。一般来说,分配对象需要符合以下原则:

4.1 对象优先在Eden分配

当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC。

4.2 大对象直接进入老年代

所谓的大对象是指:需要大量连续内存空间的Java对象。最典型的大对象就是那种很长的字符串以及数组。这样可以有效避免大对象在Eden和Survivor之间的频繁复制操作带来的性能开销。(当然我们在写代码的时候也要注意避免写出生命周期太短的大对象,因为这并不符合老年代的特点)

4.3 长期存活的对象将会进入老年代

根据老年代的特点,每当对象熬过了一次Minor GC的时候,它的age就将会增加1,当年龄增长到一定的值时(默认为15岁),它就将进入老年代。

4.4 动态对象年龄判定

对于“老年对象”的判定并不一定是要根据”默认的年龄要求(15岁)“,如果在Survivor空间中相同年龄所有对象的大小的总和大于Survivor空间的一半的时候,年龄大于或等于该年龄的对象将会直接进入老年代。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/132168.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • IDEA循环MAP的快捷键和自己常用的循环MAP方式

    IDEA循环MAP的快捷键和自己常用的循环MAP方式IDEA循环Map的快捷方式,IDEA快捷键map.keySet().iter循环输出Map的key键IDEA快捷键map.values().iter循环输出Map的key的value值//循环mapfor(Strings:map.keySet()){//输出map中keySystem.out.println(s);//获取map中key的valuemap.get(s);}这种循环不需要考虑越界问

  • PHP常见面试题_php算法面试题及答案

    PHP常见面试题_php算法面试题及答案1.PHP的基础知识点PHP中类的继承属于单继承,一个子类只能继承一个父类。可见性为publicprotected的属性和方法可以被继承。继承的方法或属性可以被重写,可见性越来越大。PHP中的变量名区分大小写,但类名、函数名不区分大小写。2.error_reporting()函数的作用error_reporting()函数的作用是临时设置脚本执行过程中的错

  • windows环境下,如何在Pycharm下安装TensorFlow环境「建议收藏」

    windows环境下,如何在Pycharm下安装TensorFlow环境「建议收藏」原文转自:https://blog.csdn.net/u012052268/article/details/74202439最近由于工作需要要使用TensorFlow,所以只能狂补相关的知识。本来博主打算在Ubantu上玩,但是由于一些原因还是放弃了这个想法,就转移到Pycharm上来玩。以下是自己在收集资料的过程中看到一篇很好的安装教程,分享一下。1.安装Anaconda选择相应的A…

  • visio2013首要事项闪退_visio一保存就闪退

    visio2013首要事项闪退_visio一保存就闪退问题描述visio2013安装完成后,新建流程图或者打开流程图,程序就会自动关闭,为了找到错误,我们打开“控制面板->事件查看器->windows事件日志”可以看到一个错误日志,错误如下:错误应用程序名称:VISIO.EXE,版本:15.0.4420.1017,时间戳:0x506742a9错误模块名称:VISLIB.dll,版本:15.0.4420.1017,时间戳:0x506741bc异常代码:0xc0000005错误偏移量:0x000000000010a887错

  • Python网络爬虫之抓取订餐信息「建议收藏」

    Python网络爬虫之抓取订餐信息「建议收藏」本文以大众点评网为例,获取页面的餐馆信息,以达到练习使用python的目的。

  • android 高德地图标记,android学习之高德地图添加标记

    android 高德地图标记,android学习之高德地图添加标记Marker用于标注地图上的一个特定位置aMap的.addMarker(markerOptions)方法即可markerOptions有如下的属性position(Required)在地图上标记位置的经纬度值。参数不能为空。title当用户点击标记,在信息窗口上显示的字符串。snippet附加文本,显示在标题下方。draggable如果您允许用户可以自由移动标记,设置为“true”。…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号