前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JVM(二)

JVM(二)

作者头像
小土豆Yuki
发布2021-08-06 14:59:03
2670
发布2021-08-06 14:59:03
举报
文章被收录于专栏:洁癖是一只狗洁癖是一只狗

什么情况下JVM内存中的一个对象被垃圾回收

正如上图,我们发现新生代有许多实例对象,而其中只有静态变量对应的实例对象有引用,其他都是没有应用的对象,并且有大量的对象,此时新生代可能都要满了,就会触发回收机制

但是我们垃圾回收之前,是要判断那些对象可以被回收,其实在JVM中给出了一种可达性分析算法,判断那些对象可以被回收,那些对象不能被回收,这个算法,就是说每个对象向上追溯,一层层判断,是否有一个GC roots

此时我们就需要知道那些对象可以当做GC roots,比如上图的静态变量,或者说局部变量都可以作为GC roots,即当你的对象被方法的局部变量,类的静态变量给应用了,就不会回收他们

此时我们需要知道对象不同的引用类型,

强引用

一个变量引用一个对象,只需要强引用的类型,那么垃圾回收的时候绝不会去回收这个对象的,如下图代码就是强引用

代码语言:javascript
复制
public class kafka {
  public static RelicaManager replicaManager= new ReplicaManager()
 }

软引用

软引用在正常垃圾回收是不会回收这个对象,只有当发现内存不够用的时候,快要发生内存溢出,就会回收垃圾回收

代码语言:javascript
复制
public class kafka1 {
public static SoftReference<ReplicaManager> replicaManager = 
new SoftReference<ReplicaManager>(new ReplicaManager());
}

弱引用

弱引用就跟没有引用是类似的,如果发生垃圾回收,就会把这个对象回收

代码语言:javascript
复制
public class kafka1 {    
public  static WeakReference<ReplicaManager> replicaManger=
new WeakReference<ReplicaManager>(new ReplicaManager());
}

虚引用

他是最弱的一种引用,他唯一的目的就是在系统回收的时候收到一个系统通知

此时我们还要知道的是假设没有GC Roots对象一定会被回收吗,看下面代码

代码语言:javascript
复制
public class ReplicaManager {
    public static ReplicaManager instance;
@Override
protected void finalize() throws Throwable {
        ReplicaManager.instance = this;
    }
}

如上RelicaManger对象要被垃圾回收的时候,他会检查这个这个对象重写了finalize(),看看是否把自己的实例对象传给了某个GC Roots变量,如果重新让GC Roots变量引用了自己,那么就不会被垃圾回收了.

JVM新生代复制算法的机制

新生代的垃圾回收,按照上面进行处理,内存都是紧凑的排列在内存中,然后进行垃圾回收,就会导致产生许多的内存碎片,此时如果大对象来了,根本存不下,因此新生代采用了一种复制算法的方式进行处理,如下图,

看到上图我们把新生代分为两部分,每部分为500MB,当进行垃圾回收的时候,把垃圾对象移动到上面的内存,存活的对象放到下面的内存中,然后清除上面内存的所有垃圾对象,这样就不会产生内存碎片,

但是,我们发现这样就会导致我们的新生代内存只有一半的内存可以使用,堆内存的使用效率太低了,因此改造成三部分,分别是Eden和Survivor1和Survivor2,

正如上图,survivor1和survivor2分贝占用100M,Eden占用800M,当有对象产生的时候,就会进入Eden和Survivor1,当这个两个区域面的时候,进行垃圾回收,就会把存活的对象放到另外一个survivor2中,这样实际内存使用位900M,同时减少了产生垃圾碎片,

但是同样我们要注意到如果存活的对象超过100M呢,survivor是存不下的,JVM会怎么办,这个后面我们详解解答

什么情况新生代的内存才会进入老年代

第一种,就是新生代快满了,就会进入老年代

第二种,进过了15次垃圾回收的对象,进入老年代,依靠参数设置,默认是15

第三种,大对象直接进入老年代,避免来回复制进入老年代,依赖参数设置默认1M

第四种,survivor的对象大小大于survivor总大小的一半,那么大于等于survivor对象年龄的对象都会进入老年代

如果新生代的存活对象内存大小大于老年代怎么办

这个不得提及老年代的空间担保机制

老年代的回收算法机制

老年代使用的是标记整理算法,进行回收机制,但是这里要注意的是老年代的回收算法比新生代要慢10倍,如下图标记整理算法,把要回收的对象移到到一半,尽量的紧凑靠在一起,然后其他进行一块清除,避免减少过多的内存碎片,如下图

常见的垃圾回收器有那些

serial和serial old 垃圾回收器

分别用来回收新生代和老年代

单线程工作,垃圾回收会停止我们写的系统的其他线程,让系统卡死不动,然后进行垃圾回收,后台系统几乎不可用

ParNew和CMS垃圾回收器

分别用来回收新生代和老年代

他们是多线程并发机制,性能更好

G1

统一收集新生代和老年代

并发收集,低停顿,性能好

Stop The World是意思

我们上面已经知道,在不同分代采用不同的垃圾回收机制,比如新生代采用复制算法,但是我们要考虑一个问题,即使进行垃圾回收时候系统还会产生对象吗,如下图

如果我们在垃圾回收的时候,还有对象不断的产生,就不太好处理了,正如我们打扫房间,但是你家狗总在制造垃圾,那一直打扫都不会清除垃圾的,因此JVM采用下面策略,垃圾回收的时候,系统对外停止使用即STW

STW对系统导致系统不能处理任何请求,正如我的系统正常请求几十ms就返回响应但是正好垃圾回收导致STW,此时十几秒都会返回响应,对用户影响极大,体验度差,我们知道内存设置不合理会导致频繁的GC,因此我们需要尽可能的减少频繁的GC,提高系统的性能

比如不同的垃圾回收器,就会对系统额影响不一样,正新生代的serival垃圾回收器是单线程回收,然后暂停工作线程,因此这种几乎不会被用到,在比如新生代的ParNew,针对多核进行利用,使用多线程,提高了性能,缩短垃圾回收时间,而老年代的CMS,他基于多线程,有一套独特的机制尽可能的减少STW的时间,可以避免长时间卡死我们的系统,

最后得出,合理的优化内存分配和垃圾回收,尽可能减少垃圾回收的频率,境地垃圾回收的时间,减少垃圾回收对系统运行的影响.

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-08-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 洁癖是一只狗 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档