如何判断对象是否需要被回收?

引用计数法,

  给对象增加一个引用计数器,当计数器为0即该对象没有被引用时,说明该对象可以被回收。但是主流Java虚拟机没有采用这种方法,主要原因是无法解决循环引用的问题。比如A引用B,B引用A,计数器均不为0,但是不能被访问到。

可达性分析法,

  主流的商用程序语言(Java、C#、Lisp)使用可达性分析法(Reachability Analysis)。基本思想就是通过一系列的称为“GC Roots”的对象作为起点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明该对象是不可达的,可以被回收。

  GC Roots对象包括:

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象。
  • 方法区中类静态属性引用的对象。
  • 方法区中常量引用的对象。
  • 本地方法栈中JNI(即一般说的Native方法)引用的对象。

逃脱回收的最后机会

  可达性分析中不可达的对象,需要检查是否要执行finalize()方法,如果对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,则标记为“没有必要执行”,会进入待回收队列。如果被判定有必要执行finalize()方法,这个对象会被放入叫做F-Queue的队列中,由虚拟机创建的低优先级Finalizer线程去触发finalize()方法,但是不保证会等待运行结束(防止一直运行导致阻塞)。如果finalize()方法中,对象重新与引用链上的任何对象简历关联,比如把自己(this)赋值给某个类变量或者对象的成员变量,那在第二次标记时将会被移出待回收集合。但是第二次进行可达性分析时,如果被标记,则直接进入待回收集合,因为任何对象的finalize()方法只会被系统自动调用一次。

  

回收方法区

  方法区(或者HotSpot虚拟机中的永久代)也是会进行回收的,Java虚拟机规范中不要求虚拟机在方法区实现垃圾收集,而且方法区垃圾收集“性价比”比较低。永久代回收主要包括:废弃常量和无用的类。废弃常量比如说字符串,没有引用的时候就会被回收。

  无用的类判断比较复杂,满足一下条件可以被回收,并且虚拟机参数可调:

  • 该类的所有实例都已经被回收,也就是Java堆中不存在该类的任何实例。
  • 加载该类的ClassLoader已经被回收。
  • 该类对应的java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

  大量使用反射、动态代理、CGLib等ByteCode框架、动态生成JSP以及OSGI这类频繁自定义ClassLoader的场景都需要虚拟机具备类写在的功能,以保证永久代不会溢出。

垃圾收集算法

标记-清除法,

  首先标记需要回收的对象,在标记完后统一回收被标记的对象。最基础的收集算法。

  不足:1.标记和清除效率都不高,2.标记清除后会产生大量不连续碎片,导致需要分配大对象时内存不够,导致另一次GC。

复制算法,

  将可用内存划分为相等的两块,每次只使用其中一块。当一块用完后,将存活的对象移到另一块内存,将之前使用的内存一次清空。

  优点:简单高效  

  不足:内存使用率低,只能使用一半的内存。

  现代商业虚拟机都在用这种算法来回收新生代,研究发现并不需要对半分,一般会分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和一块Survivor,HotSpot默认Eden:Survivor为8:1,只会有10%的空间会被浪费。

标记-整理算法,

  标记过程与标记-清楚算法的标记过程相同,后续步骤是让所有存活的对象都向一端移动,然后直接清理端边界以外的内存。

分代收集算法,

  当前商业虚拟机的垃圾收集都采用分代收集(Generational Collection)算法,根据对象存活周期的不同将内存划分为几块。一般把Java堆分为新生代和老年代,根据各个年代的特点采用适当的收集算法。新生代每次GC大批对象死亡,只有少量存活,使用复制算法。老年代中对象存活率高、没有额外空间,必须使用“标记-清理”或者“标记-整理”算法进行回收。

垃圾收集器

内存分配与回收策略

  分配规则不是百分之百确定的,其细节取决于当前使用的垃圾收集器组合,以及虚拟机中与内存相关的参数配置。

  • 对象优先在Eden分配,

    当Eden区没有足够的空间时,虚拟机将发起一次Minor GC。

  • 大对象直接进入老年代,

    大对象指,需要大量连续内存空间的Java对象,典型的是很长的字符串以及数组。大对象对虚拟机是一个坏消息,经常出现大对象容易导致内存还有不少空间时就提前出发GC。

  • 长期存活的对象将进入老年代,

     虚拟机给每个对象定义了一个对象年龄计数器。如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移到Survivor空间中,并且对象年龄设为1.每在Survivor中“熬过”一次Minor GC,年龄增加一岁,增加到一定

    程度(默认15岁),就将晋升到老年代。

  • 动态对象年龄判定

    如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需等到MaxTenuringThreshold中要求的年龄。

  • 空间分配担保

    Minor GC前会检查老年代最大可用连续空间是否大于新生代所有对象空间,如果成立则Minor GC可以确保安全。如果不成立,会检查配置是否允许失败。如果允许失败,会继续检查老年代最大可用连续空间是否大于历次晋升到老年代对象的平均大小,如

    果大于,将尝试进行一Minor GC,如果小于,或者不允许冒险,则改为进行Full GC。

refs:

《深入理解Java虚拟机》

https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html

https://docs.oracle.com/javase/9/gctuning/toc.htm

java 垃圾收集器与内存分配策略的更多相关文章

  1. Java垃圾收集器与内存分配策略

    程序的计数器.虚拟机栈.本地方法栈3个区域随线程而生,随线程而灭:栈中的栈侦随着方法的进入和退出而有条不紊地执行出栈和如栈操作. 判断对象是不是已经死亡的方法: 一.引用计数算法: 给对象添加一个引用 ...

  2. java虚拟机学习-JVM内存管理:深入垃圾收集器与内存分配策略(4)

    Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述: 说起垃圾收集(Garbage Collection,下文简称GC),大部分人都把这项 ...

  3. 深入理解java虚拟机_第三章(上)----->垃圾收集器与内存分配策略

    1.  前言 这一版块内容比较多,分为两篇文章来做笔记.本文讲述上半部分垃圾收集部分;下一篇文章写内存分配部分. 概述 对象已死吗? 引用技术算法 可达性分析算法 再谈引用 两次标记 回收方法区 2. ...

  4. 深入理解java虚拟机----->垃圾收集器与内存分配策略(下)

    1.  前言 内存分配与回收策略 JVM堆的结构分析(新生代.老年代.永久代) 对象优先在Eden分配 大对象直接进入老年代 长期存活的对象将进入老年代 动态对象年龄判定 空间分配担保  2.  垃圾 ...

  5. 《深入java虚拟机》读书笔记之垃圾收集器与内存分配策略

    前言 该读书笔记用于记录在学习<深入理解Java虚拟机--JVM高级特性与最佳实践>一书中的一些重要知识点,对其中的部分内容进行归纳,或者是对其中不明白的地方做一些注释.主要是方便之后进行 ...

  6. 《深入理解Java虚拟机》(三)垃圾收集器与内存分配策略

    垃圾收集器与内存分配策略 详解 3.1 概述 本文参考的是周志明的 <深入理解Java虚拟机>第三章 ,为了整理思路,简单记录一下,方便后期查阅. 3.2 对象已死吗 在垃圾收集器进行回收 ...

  7. 《深入理解java虚拟机》第三章 垃圾收集器与内存分配策略

    第三章 垃圾收集器与内存分配策略 3.1 概述 哪些内存需要回收 何时回收 如何回收 程序计数器.虚拟机栈.本地方法栈3个区域随线程而生灭. java堆和方法区的内存需要回收.   3.2 对象已死吗 ...

  8. Java虚拟机垃圾收集器与内存分配策略

    Java虚拟机垃圾收集器与内存分配策略 概述 那些内存须要回收,什么时候回收.怎样回收是GC须要完毕的3件事情. 程序计数器.虚拟机栈与本地方法栈这三个区域都是线程私有的,内存的分配与回收都具有确定性 ...

  9. 深入了解Java虚拟机(2)垃圾收集器与内存分配策略

    垃圾收集器与内存分配策略 由于JVM中对象的频繁操作是在堆中,所以主要回收的是堆内存,方法区中的回收也有,但是比较谨慎 一.对象死亡判断方法 1.引用计数法 就是如果对象被引用一次,就给计数器+1,否 ...

  10. GC之一--GC 的算法分析、垃圾收集器、内存分配策略介绍

    一.概述 垃圾收集 Garbage Collection 通常被称为“GC”,它诞生于1960年 MIT 的 Lisp 语言,经过半个多世纪,目前已经十分成熟了. jvm 中,程序计数器.虚拟机栈.本 ...

随机推荐

  1. 国产 Wiki 解决方案评估与选用指南

    从国产化适配视角看Wiki系统如何重塑研发知识管理 在信创产业加速落地的当下,企业级Wiki系统正经历从单纯的知识仓库向研发基础设施的关键跃迁.Gitee最新发布的<关键领域软件工程Wiki系统 ...

  2. 安防摄像头Onvif、RTSP、GB28181转web无插件直播卡顿分析

    监控摄像头网络直播 越来越多人的开始讲普通安防摄像机接入网络流媒体服务器,进行网络直播.这方面的解决方案比较多,最近测试了一个比较轻巧的解决方案:LiveNVR.这个就是实现接入普通RTSP/Onvi ...

  3. 【iOS】使用cocoapods创建私有库/公有库 2020年5月

    本文源自weicy,转载请注明出处 一般来说一个公司不只有一个产品,当项目很多后,我们就要考虑代码的共用,我们需要把一些通用的功能抽离成一个公共类,或者把一些功能做成组件,方便不同产品的使用或者分离. ...

  4. # SpringBoot3 多数据源配置与实战指南

    SpringBoot3 多数据源配置与实战指南 在实际开发中,多数据源场景越来越常见,比如读写分离.数据分片等.本文将基于SpringBoot3,结合MyBatis-Plus和dynamic-data ...

  5. 钉钉DingTalk A1“撞脸”Plaud Note:大厂匆匆入局意图何在?

    钉钉十周年新品发布会的焦点落在了其首款AI硬件DingTalk A1上,并正式宣告切入智能录音硬件赛道.这一动作不仅让全行业重新审视AI硬件的发展前景,更在社交媒体上掀起热议:不少网友指出其与Plau ...

  6. ysyx:pa3.1批处理系统

    批处理系统 为了让管理员事先准备好一组程序, 让计算机执行完一个程序之后, 就自动执行下一个程序,提出了批处理系统的思想.处理系统的关键, 就是要有一个后台程序, 当一个前台程序执行结束的时候, 后台 ...

  7. magic-api(SpringBoot可视化接口开发工具)

    1.概述 magic-api是一个基于Java的接口快速开发框架,编写接口将通过magic-api提供的UI界面完成,自动映射为HTTP接口,无需定义Controller.Service.Dao.Ma ...

  8. Electron38-Vue3OS客户端OS系统|vite7+electron38+arco桌面os后台管理

    最新研发electron38+vite7+arco-design电脑端os后台管理ElectronViteOS. vite7-electron38-os最新款vite7.1+electron38.2+ ...

  9. 发布!乘云数字参编中国信通院《运维智能体(SRE AGENT)能力要求》正式发布

    原文地址:https://databuff.com/resourceDetail/blog103 摘要: 2025年7月23日,由中国通信标准化协会主办的 "2025可信云大会" ...

  10. Meta推出Agent Learning via Early Experience,推动语言代理自主学习新范式

    原文: https://mp.weixin.qq.com/s/fhNRtk0FhK6K9_LBLwbDSg 全文摘要 在人工智能领域,语言代理(Language Agents)的自主学习能力一直是研究 ...