年轻代和老年代分别适合什么样的垃圾回收算法?

为什么要分成年轻代和老年代?

​ 因为在Java程序里,对象可以分成短期存活的对象和长期存在的对象,不同存活的时间的对象如果都放在同一个地方,会影响垃圾回收算法的效率。

大部分的正常对象,都是优先在新生代分配内存的。

public class Person{
   private static Student stu = new Student();
    public static void main(String[] args){
        loadData();
        while(true){
            fetchFromRemote();
            Thread.sleep(1000);
        }
    }
    private static void loadData(){
        Manager manager = new Manager();
        manager.load();
    }
    private static void fetchFromRemote(){
        stu.read();
    }
}

在这里插入图片描述

什么是永久代?

JVM的永久代就是之前所说的方法区,存放一些类的信息。

方法区(永久代)会不会进行垃圾回收呢?

在以下几种情况下,方法区里的类会被回收:

  1. 首先该类的所有实例对象都已经从Java堆内存被回收了。
  2. 其次加载这个类的ClassLoader已经被回收
  3. 最后,对该类的Class对象没有任何引用。

什么时候才会触发Minr GC(Young GC)呢?

在分配新的对象的时候,发现新生代的内存不足,就会触发一次垃圾回收,然后就会把所有存在新生代的垃圾对象全部给干掉,腾出大量的内存空间。

在这里插入图片描述

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

上述代码中,Student对象的实例长期被一个“Person”类的静态变量student所引用,那么就会长期存活,当触发多次young GC的时候,Student对象的实例就会被转移到老年代中。

跟JVM内存相关的几个核心差数

  1. -Xms:Java堆内存的大小
  2. -Xmx:Java堆内存的最大大小
  3. -Xmn:java新生代的大小,堆-新=老年代的内存大小
  4. -XX:PermSize:永久代大小
  5. -XX:MaxPermSize:永久代最大大小
  6. -Xss:每个线程的栈内存大小

被那些变量引用的对象是不能回收的?

JVM中使用了一种可达性分析算法来判断那些对象是可以被回收的,那些是不可以的。

这个算法的意思是,就是说对每个对象,都分析一下有谁在引用他,然后一层一层的判断,看是否有一个GC Roots。在JVM规范中,局部变量可以作为GC Roots的。例如replicaManager。

在这里插入图片描述
一句话总结:只要你的对象被方法的局部变量、类的静态变量给引用了,就不会回收他们。

Java对象的不同引用类型

  1. 强引用:只要是强引用,垃圾回收不会回收这个对象

    在这里插入图片描述

  2. 软引用:正常情况下垃圾回收是不会回收软引用对象的,但是如果内存快要溢出的时候,此时就会把这些软引用对象给回收掉,哪怕他被变量引用了,但是因为是软引用,所以还是要回收。

在这里插入图片描述
3. 弱引用

在这里插入图片描述
弱跟没引用没什么区别,发生垃圾回收就会清理掉。

  1. 虚引用 忽略 很少用

总结:没有 GC Roots引用的对象可以回收,有的话,要分是不是强、弱、软引用来分析,强不能回收,弱可以回收,软分情况,内存满回收,不满不回收

finalize()方法的使用

假设没有GC Roots引用的对象一定立马被回收吗?不是的,要看finalize()方法,

在这里插入图片描述
假设有个ReplicaManager对象要被垃圾回收了,加入这个对象重写了Object的finalize()方法,此时在回收之前会调用一下这个方法,看这个方法立马是否把这个实例对象重新引用,如果引用了就不会垃圾回收。

这个很少用到实际代码立马

对于新生代垃圾回收算法的变化

一开始是把新生代的内存分为2个区域,然后内存满的时候就会触发Minor GC,把要清理的对象标记一下,然后保留存放的对象,这会导致在内存区域东一个对象西一个对象,造成内存碎片

内存碎片造成内存浪费,碎片是离散型的,导致没有一块足够大的内存存放新的对象,这种方法是不可取的。

一个合理的垃圾回收思路 先对要存放的对象标记一下,把这些对象移到新生代的另一个内存区域,存放的时候是顺序的,这样就不会导致内存碎片的产生。
在这里插入图片描述
另外一块内存就变成空白的了,就可以继续使用。

在这里插入图片描述
这就是复制算法。这样有什么缺点呢?如果新生代的内存为1G,那么只有512MB的内存空间是可以使用的,另外一块空着,造成内存浪费,内存利用率太低。

复制算法的优化:Eden区和survivor区

把新生代的内存区域化为3部分,1个eden区,2个survivor区,默认比例为8:1:1。
在这里插入图片描述
平时可以使用就是一个Eden加上Survivor1区=900MB。

当eden区满的时候,就会触发垃圾回收,就把存活的对象放在survivor区中,因为每次存放的对象比较少,survivor可能存放的下。当下一次eden区满的时候,就会把eden存活的对象和上次存放的survivor区存活的对象一起转移到另一个survivor区。循环着用,内存利用率达到90%. 如果存放的对象大于survivor区的怎么办?这时候就会把这些对象转移到老年代中去。

在这里插入图片描述
躲过15次GC对象会进入到老年代

默认15次,参数可以配置“-XX:MaxTenuringThreshold”

大对象直接进入老年代

有一个参数"-XX:PretenureSizeThreshold"可以设置为字节数,比如“1048676”字节,就是1MB。

之所以这样做,避免躲过多次GC,造成新生代的内存浪费。来回复制也需要时间。

触发Minor GC之前会如何检查老年代可用内存大小和新生代内存大小?

首先在执行Minor GC之前,JVM会先检查一下老年代的可用空间大小,是否大于新生代所有对象的大小。大于的话,就放心的进行Minor GC,就算新生代所有对象都存放,那么老年代也是可用放的下的。

在这里插入图片描述

假如在执行Minor GC之前,发现老年代的可用内存小于新生代的所有对象,那么这个时候执行Minor GC之后,

新生代的所有对象都存放,那么老年代的可用内存放不下了,那么怎么办呢?就会看一个“-XX:-HandlePromotionFailure"的参数是否设置了,如果设置了进行下一个判断
在这里插入图片描述

老年代垃圾回收算法

标记整理算法,可达性分析活着的对象,让活的对象在内存进行移动,存活的紧靠的在一起,避免回收垃圾产生内存碎片,然后再一下子回收垃圾对象。

这个标记整理算法比新生代的回收算法慢10倍,所以full Gc代价很大。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值