JVM学习2021-02-16

Q:什么时候会触发垃圾回收?

R:新生代对象越来越多,都快满的时候就会触发垃圾回收。

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

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

在JVM规范中,局部变量就是可以作为GC Roots的,也就是只要一个对象被局部变量引用了,那么就说明他有一个GC Roots,此时就不能被回收了。

静态变量也可以看做是一种GC Roots,此时只要一个对象被GC Roots引用了,就不会回收他。

总结:只要你的对象被方法的局部变量、类的静态变量引用了,就不会回收他们。

Q:Java中对象不同的引用类型?

Java引用类型:强引用,软引用,弱引用,虚引用

强引用:一个变量引用一个对象,只要是强引用的类型,那么垃圾回收的时候绝对不会去回收这个对象的

软引用:实例对象A用一个软引用类型B的对象给包裹起来了,正常情况下垃圾回收不会回收软引用对象,但是进行垃圾回收之后,发现内存空间还是不够存放新对象,内存快溢出了,此时就会把这些软引用对象给回收掉。

弱引用:弱引用和没引用类似,发生垃圾回收,就会把这个对象回收掉。

Q:finalize()方法的作用?

如果有个对象要被垃圾回收了,假如这个对象重写了object类中的finalize()方法,那么他会首先尝试调用一下他的finalize()方法,看这个对象是否给了某个GC Roots变量。如果重新引用了那么他就不会被垃圾回收了。

 

复制算法:针对新生代的垃圾回收算法,他叫做复制算法。把新生代的内存划分成两块内存区域,然后只使用其中一块内存,待那块内存快满的时候,把里面的存活对象一次性转移到另外一块区域,保证没有内存碎片,接着一次性回收原来那块内存区域的垃圾对象。再次空出来一块内存区域。两块内存区域就这么重复着循环使用

复制算法缺点:始终只有一半内存可用,对内存的使用效率太低了。

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

新生代的内存区域划分成三块:1个Eden区,2个Survivor区。其中Eden区占内存空间的80%,两个Survivor区各占10%。

每次使用Eden区和一块Survivor区。第一次Eden区快满了,触发垃圾回收,Eden区的存活对象都一次性转移到Survivor区,然后清空Eden区。

第二次Eden区快满了,触发垃圾回收,Eden区和上次Minor GC存活在Survivor区的存活对象都一次性转移到另外一块Survivor区。然后清空Eden区和上次Minor GC存活在Survivor区。

保证始终有一块Survivor区是空着的,就这样一直循环使用着三块内存

最大的好处就是只有10%的内存空置了,剩下的90%都利用上了。

 

进入老年代判断标准

多次躲过GC进入:JVM参数“-XX:MaxTenuringThreshold”来设置,默认是15岁。:也就是说新生代每躲过一次GC被转移到一块Survivor区域中都会增长一岁。15岁的时候就会被转移到老年代中去。

动态年龄判断法:当前GC后转移到Survivor的对象中,一批对象内存超过Survivor区域内存的50%,大于这个年纪的对象全部放入老年代。

年龄1+年龄2+年龄n 的对象内存>50%Survivor区内存   然后年龄大于n的对象全部放入老年代

大对象直接进入:JVM参数“-XX:PretenureSizeThreshold”,可以把他的值设置成字节数,1048576=1MB

也就是创建大于这个大小的对象,直接进入老年代。

 

Q:Minor GC后的对象太多无法放入Survivor区怎么办?

R:需要把这些对象直接转移到老年代去

 

老年代空间分配担保规则

在执行任何一次Minor GC之前,JVM会先检查一下老年代可用的可用内存空间,是否大于新生代所有对象的总大小。

如果老年代空间小于新生代的全部对象大小,就会看JVM参数“-XX:-HandlePromotionFailure”的参数是否设置了,

设置了这个参数,判断老年代内存和之前每次Minor GC后进去老年代的对象的平均大小。

a.步骤判断失败了;

b.JVM参数“-XX:-HandlePromotionFailure”,没设置

两者满足就会直接触发Full GC,对老年代进行回收,然后再进行Minor GC。

 

a.步骤判断失败了;b.JVM参数“-XX:-HandlePromotionFailure”设置了;会出现几种可能:

第一种:Minor GC过后,存活对象大小,是小于Survivor区域大小,直接进去Survivor区域

第二种:Minor GC过后,存活对象大小,是大于Survivor区域大小,但是小于老年代可用内存大小,直接进去老年代

第三种:Minor GC过后,存活对象大小,是大于Survivor区域大小,也大于老年代可用内存大小,就会发生HandlePromotionFailure,这时候触发一次Full GC

Full GC就是对老年代进行垃圾回收,同时一般也对新生代进行垃圾回收。

 

如果Full GC过后,老年代还是没有足够的空间存放Minor GC过后的剩余存活对象,那么此时就会导致所谓的“OOM”内存溢出了

老年代垃圾回收算法:标记整理算法

老年代触发垃圾回收的时机

一是在Minor GC之前,一通检查发现很可能Minor GC 之后要进入老年代的对象太多了,老年代放不下,要提前触发Full GC 然后再带着进行Minor GC。

二是在Minor GC之后,发现剩余对象太多,放入老年代都放不下了。

 

避免频繁Full GC,要确保Survivor区有足够内存可以容纳每次Minor GC后的对象。JVM参数“-XX:-SurvivorRatio=8”这个参数默认Eden区占80%,可以调整Eden区占比,增加Survivor区的内存。避免动态年龄判断规则直接把他们升入老年代。

 

垃圾回收器:

Serial和Serial Old 垃圾回收器:分别用来回收新生代和老年代的垃圾对象

工作原理:就是单线程运行,垃圾回收的时候会停止我们自己写的系统的其他工作线程,让我们系统直接卡死不动,然后让他们垃圾回收,这个现在一般写后台的Java系统几乎不用。

ParNew和CMS垃圾回收器:ParNew现在一般都是用在新生代的垃圾回收器,CMS是用在老年代的垃圾回收器,他们都是多线程并发机制,性能更好,现在一般是线上生产系统的标配组合。

G1垃圾回收器:统一收集新生代和老年代。采用更加优秀的算法和设计机制。

 

“Stop the World”:ParNew垃圾回收器回收新生代时,是使用复制算法。需要Java系统暂停创建对象,进入“Stop the World”状态,然后垃圾回收线程调用垃圾回收器ParNew执行垃圾回收算法复制算法,把Eden区和Survivor1区的对象复制到Survivor2区,然后尽快一次性回收掉Eden区和Survivor1区中的所有垃圾对象。回收完毕就会恢复我们写的Java系统的工作线程的运行。

“Stop the World”就会造成系统停顿,Minor GC需要多久,系统的工作线程在这个时间段内不在运行,不能处理请求。

Full GC  的时间会更长,两者都会造成系统停顿,频繁卡顿会造成用户体验极差。

我们现在常用的新生代垃圾回收器是ParNew,他针对服务器一般都是多核CPU做了优化,支持多线程垃圾回收,可以大幅度提升回收性能,缩短回收时间。

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值