《深入理解JVM--第三章》--垃圾收集器与内存分配策略

《深入理解JVM–第三章》–垃圾收集器与内存分配策略

垃圾回收期GC需要完成的三件事

​ ①哪些内存需要回收

​ ②什么时候回收

​ ③如何回收

如何判定对象已死?
判定对象存亡有两种方案:①引用计数算法、②引用链法
引用计数算法

策略:给每一个对象添加一个引用计数器,当有地方引用这个对象时,计数器+1,当这个引用失效时,计数器-1,当任何情况引用计数器都为0时,该对象失效

存在的问题:对象间相互循环引用

引用链法

策略:通过一系列叫做GC Root的对象作为起点,从这些节点开始往下搜索,所走过的路径称为引用链,当一个对象没有到GC Root的引用链时,判断为对象死亡

可做GC Root的对象

​ 1)虚拟机栈中引用的对象

​ 2)方法区中类静态属性所引用的对象

​ 3)方法区中常量引用的对象

​ 4)本地方法中引用的对象

生存还是死亡?

在引用链中就算不可达GC Root的对象,也不是就一定是死亡的对象,如果一个对象要真的死亡,那么它至少要经历两次标记的过程

在这里插入图片描述

重新连接例子(例如对象本身(this)赋值给一个类变量或者对象的成员变量),finalize方法只能被调用一次。

Java中四种引用类型

强引用:如果一个对象具有强引用,那么GC是不会回收它的,即使内存溢出都只会报异常而不会回收该对象

软引用:如果一个对象是软引用,当内存空间足够时,GC不会去回收它,但是当内存空间不够时,则会回收这些对象所占有的内存空间。软引用用于内存敏感的 高速缓存

弱引用:如果一个对象是弱引用,那么该对象只能生存到下一次GC触发之前

虚引用:虚引用比较特别,它不是为了引用对象,而是为了跟踪对象被GC回收的活动。虚引用必须跟一个引用队列一个搭配使用。当GC对一个有着虚引用的对象 进行回收时,这个回收不是马上回收,而是先将虚引用加入一个引用队列当中,通过判断引用队列是否加入了虚引用来判定该引用对象是否将要被回收, 这样就可以在对象回收之前做一些操作。

弱引用在ThreadLocal中的使用(ThreadLocal在线程高并发中)

我们知道Thread对象里面有一个ThreadLocalMap对象,当我们创建一个ThreadLocal对象时候(例如 ThreadLoca tl=new ThreadLocal()),对象实例tl指向这个ThreadLocal对象内存,是一个强引用,当tl调用其内部set方法时候(即tl.set(new Person()),那么tl就会调用ThreadLocal中set(t VALUE)方法,返回的是一个ThreadLocalMap对象map,此时map中存储着当前ThreadLocal对象(即tl)的key就指向了这个内存,但是注意,如果这个引用用强引用的话,那么当我们利用GC对tl对象进行回收时候,由于还有key这个引用引用着这块内存空间,那么这块内存空间就不会随着new ThreadLocal该对象引用的失效而回收,因此这个key这个对象对这块内存空间的引用需要用弱引用,而由于map中的value仍然指向这个person这块内存空间,这部分就没有会回收,因此在执行完tl之后,应该调用tl.remove()这个方法,直接把这个entey给去掉。

在这里插入图片描述

垃圾收集算法
1)标记-清除算法

​ 算法分为**“标记”“清除”**两个操作,首先会对要被回收的对象进行标记,然后将这部分被标记的对象进行清除

​ 不足:效率低下,会产生大量不连续的空间碎片

2)复制算法

​ 复制算法将内存分为两块*(注意,这部分内存指的是新生代内存,因为JVM一般将堆中的内存分为新生代、老年代和永久代,而新生代正是用来存放新生的 对象,一般占据堆的1/3的内存,也正是由于频繁的创建对象所以会触发minor GC进行垃圾回收)*,每次使用的是其中的一块,当这一块内存用完之后,会 将还存活的对象进行复制,复制到另一块空间当中,然后对使用的这块空间进行一次清除操作。但是只是为了垃圾回收就将内存空间压缩一半的话明显是浪 费了内存,所以一般这个分配不是按照1:1分配,而是将新生代分为一个Eden区、ServivorFrom区和ServivorTo三个区。一般这三个区的分配比为Eden: ServivorTo:ServivorFrom=8:1:1。 当发生回收时,Eden区和 ServivorTo区的存活对象会一次性复制到ServivorFrom区,然后对Eden区和ServivorTo区 进行回收。当需要复制的对象如果大于10%(即ServivorFrom区所占的空间)时,这里会采用一种担保策略,会将这部分对象会直接进入老年代,老年代 的对象比较稳定。

3)标记整理法

​ 和标记-清除法一样一开始也是对要清除的对象进行标记,但是它不是对可回收对象进行直接清除,而是让所有存活的对象都向一边移动,然后直接清理掉 端边界以外的内存。

垃圾收集器

​ 收集算法是方法理论,收集器是内存回收的具体实现

1)Serial收集器

​ client模式下的单线程收集器,这个是用一条线程去完成垃圾收集工作,而且要暂停其它工作线程

2)parNew收集器

​ 是Serial的多线程版本,随着CPU的增加而显示出优势

3)Parallel Scavenge收集器

​ 它是新生代收集器,使用的是复制算法,并行的多线程收集器。与ParNew相似,但是它是侧重吞吐量(吞吐量=用户代码运行时间/CPU消耗的总时间),可 以控制垃圾收集停顿时间,最大效率的利用CPU时间。(就像并发编程中控制锁的拥有时间一样)、

4)Serial Old收集器

​ 它是运行在老年代的Serial收集器,同样是单线程收集器,但它使用的是标记-整理算法

5)Parallel Old

​ 它是Parallel Old收集器的老年代版本,使用多线程,标记-整理算法。

6)CMS收集器

​ 它是老年代收集器,采用标记-清除算法,是一种获取最短回收停顿时间为目标的收集器,非常符合注重用户体验的应用上使用。

吞吐量优先和响应优先的垃圾收集器如何选择?
1)吞吐量优先

​ 新生代Parallel Scavenge,老年代使用Parallel Old,并配置多个线程进行回收,设置参数来调整最大垃圾收集停顿时间和吞吐量的大小。

​ 2)响应优先

​ 设置老年代的收集器是CMS(最短时间,Spark streaming采用的这个),新生代是Par New(多线程)

内存分配与回收策略

​ 大多数情况下,对象是在新生代中Eden区分配。当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC

Minor GC、Major GC、Full GC的区别

​ minor Gc:指的是发生在新生代的GC,它发生的频率非常频繁,因此它的处理时间也是非常的短

​ Major GC:发生在老年代的GC

​ Full GC:清理整个空间包括新生代和老年代。

什么时候对象进入老年代

​ 1)大对象直接进入老年代:占用大量连续内存空间的java对象,直接进入老年代

​ 2)长期存活的对象进入老年代:虚拟机给每一个对象设置了一个年龄计数器(Age),当对象发生一次GC后并且存活下来之后,那么这个Age就会增加1,当 年龄增加到一个值时会被进入老年代(跟修仙一个道理)。或者使用动态对象年龄判定,如果Survivor空间中相同年龄的对象内存总和大于Survivor空间 的一半,则年龄大于或等于该年龄的对象就会直接进入老年代,无需达到设定的年龄。

空间分配担保策略

​ Eden区的对象在发生GC之后会复制到ServivorFrom区,当ServivorFrom区空间满了之后,在Eden还存活的对象会直接被分配到老年代。

等于该年龄的对象就会直接进入老年代,无需达到设定的年龄。

空间分配担保策略

​ Eden区的对象在发生GC之后会复制到ServivorFrom区,当ServivorFrom区空间满了之后,在Eden还存活的对象会直接被分配到老年代。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值