关于GC(中):Java垃圾回收相关基础知识

Java内存模型

关于GC(中):Java垃圾回收相关基础知识
(图源: 深入理解JVM-内存模型(jmm)和GC)

区域名 英文名 访问权限 作用 备注 程序计数器 Program Counter Register 线程隔离 标记待取的下一条执行的指令 执行Native方法时为空; JVM规范中唯一不会发生OutOfMemoryError的区域 虚拟机栈 VM Stack 线程隔离 每个Java方法执行时创建,用于存储局部变量表,操作栈,动态链接,方法出口等信息 方法执行的内存模型 本地方法栈 Native Method Stack 线程隔离 Native方法执行时使用 JVM规范没有强制规定,如Hotspot将VM和Native两个方法栈合二为一 Java堆 Java Heap 线程共享 存放对象实例 更好的回收内存 vs 更快的分配内存 方法区 Method Area 线程共享 存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据 JVM规范不强制要求做垃圾收集 运行时常量池 Runtime Constant Pool 线程共享 方法区的一部分 直接内存 Direct Memory – 堆外内存,通过堆的DirectByteBuffer访问 不是运行时数据区的一部分,但也可能OutOfMemoryError

对象的创建——new的时候发生了什么

讨论仅限于普通Java对象,不包括数组和Class对象。

  1. 常量池查找类的常量引用,如果没有先做类加载
  2. 分配内存,视堆内存是否是规整(由垃圾回收器是否具有压缩功能而定)而使用”指针碰撞”或”空闲列表”模式
  3. 内存空间初始化为零值,可能提前在线程创建时分配TLAB时做初始化
  4. 设置必要信息,如对象是哪个类的示例、元信息、GC分代年龄等
  5. 调用 <init></init>方法

垃圾回收器总结

垃圾回收,针对的都是堆。

分代

  • 新生代:适合使用复制算法, 以下三个区一般占比为8:1:1
  • Eden 新对象诞生区
  • From Survivor 上一次GC的幸存者(见”GC种类-minor GC”)
  • To Survivor 本次待存放幸存者的区域
  • 老年代:存活时间较久的,大小较大的对象,因此使用标记-整理或标记-清除算法比较合适
  • 永久代:存放类信息和元数据等不太可能回收的信息。Java8中被元空间(Metaspace)代替,不再使用堆,而是物理内存。

分代的原因

  • 不同代的对象生命周期不同,可以针对性地使用不同的垃圾回收算法
  • 不同代可以分开进行回收

回收算法

名称 工作原理 优点 缺点 标记-清除 对可回收对对象做一轮标记,标记完成后统一回收被标记的对象 易于理解,内存利用率高 效率问题;内存碎片;分配大对象但无空间时提前GC 复制 内存均分两块,只使用其中一块。回收时将这一块存活对象全部复制到另一块 效率高 可用空间减少; 空间不够时需老年代分配担保 标记-整理 对可回收对对象做一轮标记,标记完成后将存活对象统一左移,清理掉边界外内存 内存利用率高 效率问题 标记-X算法适用于老年代,复制算法适用于新生代。

GC种类

  • Minor GC,只回收新生代,将Eden和From Survivor区的存活对象复制到To Survivor
  • Major GC,清理老年代。但因为伴随着新生代的对象生命周期升级到老年代,一般也可认为伴随着FullGC。
  • FullGC,整个堆的回收
  • Mixed GC,G1特有,可能会发生多次回收,可以参考关于G1 GC中Mixed GC的分析

垃圾回收器小结

垃圾回收器名称 特性 目前工作分代 回收算法 可否与Serial配合 可否与ParNew配合 可否与ParallelScavenge配合 可否与SerialOld配合 可否与ParallelOld配合 可否与CMS配合 可否与G1配合 Serial 单线程 新生代 复制 – – – Y N Y N/A ParNew 多线程 新生代 复制 – – – N N Y N/A ParallelScavenge 多线程, 更关注吞吐量可调节 新生代 复制 – – – N N Y N/A SerialOld 单线程 老年代 标记-整理 – – – Y Y N N/A ParallelOld 多线程 老年代 标记-整理 N N Y – – – N/A CMS 多线程,并发收集,低停顿。但无法处理浮动垃圾,标记-清除会产生内存碎片较多 老年代 标记-清除 Y Y N Y – – N/A G1 并行并发收集,追求可预测但回收时间,整体内存模型有所变化 新生代/老年代 整体是标记-整理,局部(两Region)复制 N N N N N N –

在本系列的上一篇文章关于GC(上):Apache的POI组件导致线上频繁FullGC问题排查及处理全过程中,减少FullGC的方式是使用G1代替CMS,计划在下一篇文章中对比CMS和G1的区别。

理解GC日志

只举比较简单的例子,具体各项的格式视情况分析,不同回收器也会有差异。

2019-11-22T10:28:32.177+0800: 60188.392: [GC (Allocation Failure) 2019-11-22T10:28:32.178+0800: 60188.392: [ParNew: 1750382K->2520K(1922432K), 0.0312604 secs] 1945718K->198045K(4019584K), 0.0315892 secs] [Times: user=0.09 sys=0.01, real=0.03 secs]

开始时间-(方括号[)-发生区域(ParNew,命名和GC回收器有关)-回收前大小-回收后大小-(方括号])-GC前堆已使用容量-GC后堆已使用容量大小-回收时间-使用时间详情(用户态时间-内核时间-墙上时钟时间)

注意这里没有包括”2019-11-22T10:28:32.177+0800: 60188.392: [GC (Allocation Failure)”这部分的分析。

可借鉴的编程模式

对象分配的并发控制

对象创建是很频繁的,在线程共享的堆中会遇到并发的问题。两种解决办法:

  1. 同步锁定:CAS+失败重试,确保原子性
  2. 堆中预先给每个线程划分一小块内存区域——本地线程分配缓冲(TLAB),TLAB使用完并分配新的TLAB时才做同步锁定。可看作1的优化。

CAS: Conmpare And Swap,用于实现多线程同步的原子指令。 将内存位置的内容与给定值进行比较,只有在相同的情况下,将该内存位置的内容修改为新的给定值。关于CAS可以参考:
Java中的CAS实现原理
CAS系列(3):CAS无锁自旋和同步锁线程切换使用场景对比

对象访问的定位方式

前提条件:通过 上本地变量表的reference访问 中的对象及它在 方法区的对象类型数据(类信息)
主流的两种方式,这两种方式各有优点,可以看出方式2是方式1的优化,但并不是全面超越方式1,无法完全取代。
这里可以看到要权衡垃圾回收和访问速度两方面。

方式1: 直接指针访问实例数据

关于GC(中):Java垃圾回收相关基础知识
图源:深入理解JVM-内存模型(jmm)和GC
reference直接存放对象实例地址,只需要一次访问即可,执行效率较高。

方式2: 使用句柄池

关于GC(中):Java垃圾回收相关基础知识
图源:深入理解JVM-内存模型(jmm)和GC
reference中地址稳定,对象被移动时只需要改句柄池的地址。相对的,访问实例需要两次指针定位。

参考资料

  1. 周志明.著《深入理解JAVA虚拟机》
  2. 深入理解JVM-内存模型(jmm)和GC
  3. jvm的新生代、老年代、永久代关系
  4. JVM垃圾回收——新生代,老年代,永久代,Minor GC,Full GC

Original: https://www.cnblogs.com/wuyuegb2312/p/11839171.html
Author: 五岳
Title: 关于GC(中):Java垃圾回收相关基础知识

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/539200/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 官网VMware下载安装教程(超详细)

    首先在游览器搜索VMware,进入官网。或者直接点击VMware 然后点击工作空间,再点击WorkStation Pro 进入以下界面 下滑到最下面,点击下载试用版 页面发生跳转,…

    Java 2023年6月5日
    0108
  • 复试-英语自我介绍-模板参考

    英语自我介绍模板,我没用到……分享给网友。 Good morning, my distinguished professors. It’s my…

    Java 2023年6月5日
    079
  • java 获取获取字符串编码格式

    public static String getEncoding(String str) {String encode = “GB2312”;try {if…

    Java 2023年5月29日
    062
  • Fiddler高级用法

    Fiddler高级用法 1. 简单用法 Fiddler作为一个基于http协议的抓包工具,一直在业界有广泛使用。很多测试或者前端在使用Fiddler时,仅仅用于查看前端和服务端之间…

    Java 2023年6月13日
    051
  • 全链路追踪 & 性能监控工具 SkyWalking 实战

    Skywalking介绍 Skywalking是一个国产的开源框架,2015年有吴晟个人开源,2017年加入Apache孵化器,国人开源的产品,主要开发人员来自于华为,2019年4…

    Java 2023年6月8日
    066
  • 从0到1写一款自动为Markdown标题添加序号的Jetbrains插件

    1. markdown-index 最近做了一个Jetbrains的插件,叫 markdown-index,它的作用是为Markdown文档的标题自动添加序号,效果如下: 目前已经…

    Java 2023年6月7日
    093
  • JeeSite Spring Cloud安装搭建

    引言 JeeSite Cloud 具备 JeeSite 4.x 的所有功能,是在 JeeSite 4.x 基础之上,完成的 Spring Cloud 分布式系统套件的整合。它利用 …

    Java 2023年6月5日
    081
  • jdk8函数式接口——Consumer介绍

    Consumer介绍与实例分析函数式接口:@FunctionalInterfaceConsumer(消费者)函数式接口:@FunctionalInterface自从jdk8提供了函…

    Java 2023年5月30日
    060
  • 知乎问题:.NET AOT编译后能替代C++吗?

    标题上的Native库是指:Native分为静态库( (2)Remobjects Elements (https://www.remobjects.com/elements/) 收…

    Java 2023年6月5日
    0105
  • TestLink在线Excel用例转换xml

    项目功能 TestLink在线Excel用例转换xml将符合用例模板的Excel测试用例,转换成xml,用于导入TestLink进行用例管理。 使用方法 1、编写测试用例 2、打开…

    Java 2023年6月6日
    0116
  • 【工具-Nginx】从入门安装到高可用集群搭建

    文章已收录至https://lichong.work,转载请注明原文链接。ps:欢迎关注公众号”Fun肆编程”或添加我的私人微信交流经验🤝 一.Nginx安…

    Java 2023年6月9日
    071
  • [Java]Map接口有关面试题

    Map接口 1、HashMap和Hashtable的区别 线程安全方面。 HashMap是非线程安全的, Hashtable是线程安全的。因为 Hashtable内部方法基本都经过…

    Java 2023年6月5日
    0136
  • 个人学期总结

    个人学期总结 本学期共发表博客93篇 开正题之前先发表一下感慨,说实话,这个学期真的过的很快,转眼间就要大三了,直到现在,我还能清楚的记得这个学期我大概的经历,我是3月1号来的学校…

    Java 2023年6月7日
    075
  • 从XXE漏洞修复引起Not supported: http://javax.xml.XMLConstants/property/accessExternalDTD说到SPI机制

    引子 在使用Fortify扫描时代码报XML External Entity Injection,此漏洞为xml实体注入漏洞,XXE攻击可利用在处理时动态构建文档的 XML 功能。…

    Java 2023年6月13日
    0104
  • Java(2)数据类型与变量

    前言 Java是一种强类型语言。这就意味着必须为每一个变量声明一种类型。在Java中,一共8种基本类型,其中有4种整型、2种浮点型、1种字符串类型char(用于表示Unicode编…

    Java 2023年6月9日
    070
  • ch02 JAVA入门

    JAVA入门 JAVA特点 语法和C语言类似 没有指针和内存管理 高度可移植性,运行在JVM虚拟机上 面向对象 类型安全 高质量的类库 JAVA的平台、系统和工具 构建工具:Ant…

    Java 2023年6月9日
    076
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球