java垃圾收集器和内存分配

本文深入探讨Java垃圾收集器的工作原理,包括引用计数、可达性分析算法、四种引用类型及垃圾收集算法如标记清除、复制算法等。同时,介绍了Serial、ParNew、Parallel Scavenge、CMS和G1收集器的特点与应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

java垃圾收集器

java垃圾收集器在一开始需要考虑完成三个问题:1.哪些内存需要回收,2.什么时候回收,3.如何回收。在java中第一件事就是确定哪些对象还存活,哪些对象需要回收,所以引出了不同策略算法

引用计数算法

给对象添加一个引用计数,当一个地方引用时,计数器加1,当引用无效时,减1,直到引用计数为0,但是缺点是很难解决对象之间相互循环引用问题

可达性分析算法

通过一系列称为“GC Roots”的对象作为起始点,从这些节点开始搜索,搜索的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,表示该对象不存活
可作为GC Roots的对象有:虚拟机栈中引用的对象,方法区中类静态属性引用的对象,方法区中常量引用的对象,本地方法栈中JNI引用的对象

引用

java中垃圾回收器收集无用的对象时,是无轮是何种算法,都是判断该对象是否有其他对象保持了对该对象的引用,如果没有,gc就会回收该对象。在现实中,我们需要保存另外一种情况,比如当内存充足时,保留该对象,当内存不够时,再回收,而这些在java语言中给了我们另外的处理,将引用分为 强引用,软引用,弱引用,虚引用,这4中引用

  • 强引用:就是普通的创建对象过程“Object obj=new Object()”,这些对象永远不会被垃圾收集器回收
  • 软引用:在系统内存溢出之前,会对这些对象进行回收,java中提供了SoftReference类来实现软引用
  • 弱引用:当垃圾回收器回收对象时,不管内存是否足够,都会回收处理,java提供了WeakReference类来实现
  • 虚引用:一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,目的是当对象被回收时,可以得到系统通知,java中提供了PhantomReference来实现

垃圾收集算法

  • 标记清除算法:标记-标记出需要回收的对象,清除- 清理前面标记过的对象
    • 缺点:1.效率不高(标记和清除过程效率不高),2.标记清除会产生不连续的内存碎片
  • 复制算法:将内存按容量大小分为大小相等的两块,每次使用完其中的一块时,将还存活的对象复制到另一块上,然后清除使用过的内存。一种分配策略为:将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden空间和一块Survivor空间,当回收时,将还存活的对象一次性复制到另一块Survivor上。hotSpot上默认的比例为8:1
  • 标记整理:第一阶段标记,第二阶段整理,将存活的对象向一端移动,然后直接清理掉端边界以外的内存
  • 分代收集算法:对不同区域的内存使用不同的算法,比如:新生代:每次垃圾收集时只有少量对象存活,选择复制算法,对于老年代:由于对象存活率高,所以使用标记整理更适合

stop the world

当jvm执行可达性分析时,发生的java执行线程停顿,主要是为了保证在整个分析期间,整个执行系统不可以出现在分析过程中对象引用关系不断变化的情况,所以导致GC进行时必须停止所有的java执行线程

oopMap

在jvm中如何可以更快的知道GC的根枚举,GC Roots主要在全局性的引用(常量或类静态属性)与执行上下文(本地变量表中的引用)中,很多应用上百兆,如果进行遍历查找,效率会非常低下。所以,jvm中使用OopMap数据结构来存储哪些地方存放着对象的引用,在类加载完成后,jvm就将对象内什么偏移量是什么数据类型计算出来,保存到OopMap中,在jit编译时,在特定的位置记录栈和寄存器中哪些位置是引用,这样在GC时,就可以直接得到信息

安全点

jvm会在特定的位置记录这些类对象地址信息,这个特殊位置称为安全点,既程序执行到达安全点时才能暂停,开始GC回收垃圾,在多线程中,垃圾回收器会等待所有线程进入安全点后才回收垃圾,多线程情况下主要有两种方案可供选择:1.抢先式中断和主动式中断

  • 抢先式中断:在GC发生时,首先将所有线程全部中断,如果有线程中断的地方不在安全点,就恢复线程,继续跑到安全点
  • 主动式中断:当GC需要中断时,设置中断标志,当线程执行到安全点时轮询标志,发现中断标志就中断挂起

安全区域

当线程处于sleep或blocked状态时,线程无法响应jvm的中断请求,所以出现安全区域,安全区域是指:在一段代码片段中,引用关系不会发生变化,在这个区域内任何地方开始GC都是安全的。当线程执行到安全区域时,首先标示进入了安全区域,当线程要离开安全区域时,先要检查是否完成根节点枚举,如果完成了就继续执行,否则就等待直到可以安全离开为止

垃圾收集

  • serial收集器:serial收集器是一个单线程的收集器,它在工作时必须stop the world,直到收集结束
  • ParNew收集器:使用多线程进行垃圾收集,就是多线程版本的serial收集器
  • Paraller scavenge收集器:Paraller 收集器是一个新生带收集器,采用复制算法,采用多线程标记。此收集器目标是达到一个可控制的吞吐量(吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间))。所以主要适合在后台运算而不需要太多交互的任务,可以通过-XX:MaxGCPauseMillis 设置垃圾收集最大停顿时间,虚拟机将会调整堆的大小来适应GC的停顿时间,和-XX:GCTimeRatio设置吞吐量参数:吞吐量为垃圾回收时间与非垃圾回收时间的比值,公式为1/(1+N)。例如,-XX:GCTimeRatio=19时,表示5%的时间用于垃圾回收。默认情况为99
  • serial old 收集器:serial old 收集器是在老年代区域运行的收集器,是一个单线程收集器,使用标记整理算法,
  • Paraller Old 收集器:他是Paraller scavenge收集器的老年版本,使用多线程和标记-整理算法
  • cms收集器:cms收集器是以获取最短回收停顿时 间为目标的收集器,它是以标记-清除算法实现的,主要过程为:1.初始标记(标记GCRoot 能直接关联的对象) 2.并发标记(进行GC root 重新追溯),3.重新标记(修正并发标记产生的误差) 4.并发清除(清除),其中初始标记和重新标记是需要stop the world。但是cms有三个明显的缺点:1.cms收集器对cpu资源敏感,2.cms无法处理浮动垃圾(在并发清理过程中产生的垃圾) 3.cms是基于标记-清除算法的,会产生空间碎片
  • G1收集器:G1收集器是目前最新的垃圾收集器,它的特点有:1.并发与并行,2.分代收集,3.空间整合:不会产生的空间碎片 4.可预测停顿。G1收集器将java内存堆分为大小相等的独立区域,新生代和老年代都是一部分区域的集合。G1收集器在进行垃圾收集的时候,他是有计划地避免在整个java堆中进行全区域的垃圾收集,G1维护了一个不同区域的优先列表,每次根据允许的收集时间,优先回收价值最大的区域
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值