查看原文
其他

聊一聊新生代和老年代垃圾回收的细节

点击上方 "程序员小乐",关注公众号

8点20分,第一时间与你相约

每日英文

You deserve to be with someone who makes you happy. Somebody who doesn't complicate your life. Somebody who won't hurt you.

你应该找一个能让你快乐的人,不让你的生活变复杂,不会伤害你。


每日掏心话

越好的人,就越会被嫉恨,被打击。所以,当你承受压力时,千万别让自己融入平庸,不用去跟别人解释自己,关于未来只有自己明白。


来自:正号先生 | 责编:乐乐

链接:cnblogs.com/zhxiansheng/p/11294529.html

图片来自网络


往日回顾:Deepin软件太少,如何安装Windows软件?



   正文   



1 新生代和老年代


对象在被实例化之后,都是属于新生代。


大部分新生代的生命周期都是及其短暂的,例如在一个方法中创建的对象会随着方法执行完毕,栈空间的栈帧出栈后而失去引用。


而有一些对象确实会长期存活在堆内存的,比如被Static引用的对象。这种对象不会轻易的被垃圾回收器回收。


所以JVM会将堆内存分为两个区域,一个年轻代,一个老年代。


其中年轻代,顾名思义,就是创建和使用完之后立马就要被回收的对象放在里面。然后老年带呢,就是把一些会长期存活的对象放在里面。


1.1 为什么要分成新生代和老年代


很多人都会有一个疑问,为什么要划分两个区域呢?


因为这个垃圾回收有关,对于年轻代的对象,他们的特点是很快就会被回收,所以需要使用一种垃圾回收算法。


而对于老年代而言,里面的大部分对象可能都会长期存活,那么使用新生代的回收算法放在这里就可能并不是那么的合适。需要有着自己的一套回收算法。

2 什么是永久代


很简单,JVM里的永久代其实就是我们之前说的方法区


所谓的永久代,你可以认为是存放一些类的信息,在上一个章节我们知道我们生成的.class就是存放在这个区域的。一般情况下,我们对于jvm调优都是对新生代和老年代进行调优。一般而言永久代保持默认配置就可以了。


是不是意味着我们就不需要关注永久代?


肯定不是的。因为要存储类的相关信息,所以对于动态生成类的情况比较容易出现永久代的内存溢出。最典型的场景就是,在 jsp 页面比较多的情况,容易出现永久代内存溢出。

3 如何判断一个对象是否是垃圾


我们知道,当一个对象被创建出来的时候,比如说在一个方法中创建一个对象,当该方法执行完毕后,就没有引用指向这个对象了,这个对象就会变成垃圾对象。


这仅仅是一种情况。


到低哪些对象是辣鸡,哪些对象不是?


JVM中使用了一种可达性分析算法来判定哪些对象是可以被回收的。这个算法的核心就是看这个对象有谁在引用它,然后一层一层的往上判断,看是否被GC roots所引用。


在java中,可作为GC Roots的对象有:

1.虚拟机栈(栈帧中的本地变量表)中引用的对象(也就是我们前面提到在方法中创建的对象);2.方法区中的类静态属性引用的对象;3.方法区中常量引用的对象;4.本地方法栈中JNI(即一般说的Native方法)中引用的对象

当一个对象没有被上述所引用,那么这个对象就可以被认为是垃圾对象了。


总之记住一句话,只要你的对象被方法的具备变量,类的静态变量给引用了,就不会回收他。

3.1 java中对象不同的引用关系。


1、强引用

就是被GC roots所直接引用的对象。只要是强引用关系,那么垃圾回收器是绝对不会回收这个对象的。

2、软引用

public class test{public static SoftReference<ReplicaManager> manager =new SoftReference<ReplicaManager>(new Replicamanager)}
/**如上诉代码,实例对象被“SoftReference” 软引用类型的对象包装起来了,那么这个对象的引用就是软引用。
正常情况下是不会回收软引用对象的,但是如果你进行垃圾回收后,返现内存还是不够存放新的对象的时候,这个时候就会吧软引用的对象给回收掉**/


3、弱引用

这个相对前两中使用的很少,与软引用类时,当一个实例对象被“WeakReference”弱引用的对象包装起来的时候,那么这个对象就是弱引用。弱引用的生命周期就存在下一次垃圾回收之前,也就是说下一次垃圾回收会回收掉弱引用的对象。


4、虚引用

最弱鸡的一种引用,我感觉没什么作用。一个对象是不是虚引用对他本生的生命周期没有影响。该什么时候回收就什么时候回收。设置他唯一的目的就是被回收的时候会得到一个系统通知,一般来说没什么卵用


3.2 finalize()方法的作用


到这里,我相信你应该清楚了哪些对象会被回收哪些对象不会被回收。


如果一个对象被GC Roots所引用,但是!他如果是软引用或弱引用,那么也是可能会被辣鸡回收器给回收掉的。


在垃圾对象被回收的时候,会调用Object对象的finalize()方法。


我们来模拟一个辣鸡的自我救赎代码。

public class A{public static A instance;@Overrideprotected void finalize() throws Throwable{ A.instance = this; }}

当对象被回收的时候,调用finalize方法,重新将GC Roots的变量引用指向自己,那么就不会被回收了。


这个基本上没人用,感觉就是一个sb才会写的代码。写出来就是给大家说出这个细节。

4 新生代对象是如何变成老年代对象的


4.1 长期存活的对象会多次躲过垃圾回收



躲过多少次Minor GC而没有被回收掉,我们就认为这个对象的年龄有几岁了,默认情况下,当一个对象10多岁的时候,就认为他是一个老人了。需要被转移到老年代去。

想想也正常,新生代的竞争压力这么大,老年对象还是早点去老年代比较好不与年轻人竞争资源。

4.2 特别大的超大对象直接不经过新生代就进入老年代


年轻代的的竞争本来就很大,你还要占用那么多资源,不行不行,你还是去老年代吧。

4.3 动态年龄判断机制


这一部分需要结合垃圾回收器的算法来讲,就是复制算法。我们在后面的章节会讲到,这里大概介绍一下。


我们一般会将新生代分为三个区域,一个Eden,两个Survivor。比例8:1:1.


生成的对象默认在eden区域。当发生一次Minor GC后,会将存活的对象复制到其中一个Survivor区域。当下一次GC后又会将存活的对象复制到另一块Survivor。这么做的好处是减少内存碎片。


当我们发生一次GC后,将存活对象放到其中一块survivor区域。发现其中的1岁,2岁,3岁的对象年龄加起来内存超过survivor区域的一半,就会把4岁以及4岁以上的对想转移到老年代。

4.4 GC后survivor区域存放不下


这个没办法,GC后都还存放不下一般来说要不是访问量激增,要么就是优化的不到位,所以只好将这写对象转移到老年代。

4.5 空间担保机制


在发生minor gc之前,虚拟机会检测 : 老年代最大可用的连续空间>新生代all对象总空间?


1、满足,minor gc是安全的,可以进行minor gc。

2、不满足,虚拟机查看HandlePromotionFailure参数:


(1)为true,允许担保失败,会继续检测老年代最大可用的连续空间>历次晋升到老年代对象的平均大小。若大于,将尝试进行一次minor gc,若失败,则重新进行一次full gc。

(2)为false,则不允许冒险,要进行full gc(对老年代进行gc)。

欢迎在留言区留下你的观点,一起讨论提高。如果今天的文章让你有新的启发,学习能力的提升上有新的认识,欢迎转发分享给更多人。


欢迎各位读者加入程序员小乐技术群,在公众号后台回复“加群”或者“学习”即可。


猜你还想看


阿里、腾讯、百度、华为、京东最新面试题汇集

学不会二分搜索树?放弃?我偏不...

神么?找女朋友还需要掌握算法?我们太难了...

面试官问:一个 Java 字符串中到底有多少个字符?...

24 个必须掌握的数据库面试问题!

对于HTTP过程中POST内容加密的解决方案

关注「程序员小乐」,收看更多精彩内容
嘿,你在看吗?

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存