GC入门和JAVA内存

GC算法

一、对象存活判断

在正式进入GC算法之前先要了解JVM是怎么在GC过程判断对象存活的。

常见的判断方法有以下两种

1. 引用计数法

简而言之就是给 Java 对象添加一个引用计数器,每当有一个地方引用它时,计数器 +1;引用失效则 -1。当GC发生时把引用为0的全部清理掉

比如:

A a = new A();
B b = new B();
a.b = b;

对象a引用了对象b,a的计数器为0,b的计数器为1,当GC发生时a被回收,b的计数器-1变成0,下一次GC发生时b就会被回收。

这个方法虽然简单粗暴,但是有一个致命性的问题,那就是无法判断循环引用

A a = new A();
B b = new B();
a.b = b;
b.a = a;

比如遇到这种情况,即使a和b没有被其他对象引用,也无法被回收。

2. 可达性分析

将一系列的 GC Roots 对象作为起点,从这些起点开始向下搜索,所有在引用链中的对象即为存活对象。

可作为 GC Root 的对象有:

  1. Java虚拟机栈(栈帧的本地变量表)中引用的对象
  2. 本地方法栈 中 JNI引用对象
  3. 方法区 中常量、类静态属性引用的对象

二、GC算法

1. 复制算法

复制算法将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。

复制算法的问题有两个:

  • 空间浪费,内存被分为2部分
  • 效率不稳定,当存活对象过多的时候,效率会非常低

2. 标记-清除算法

顾名思义,这种方法分为两个步骤,标记和清除

  • 标记步骤:GC开始的时候,JVM先从GC Roots开始遍历整个堆,将存活对象进行标记
  • 清除步骤:将未被标记的所有对象全部清理掉

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O4lOKN0p-1691029166510)(https://joshua-1301529810.cos.ap-chengdu.myqcloud.com/img/%E6%A0%87%E8%AE%B0-%E6%B8%85%E9%99%A4%E7%AE%97%E6%B3%95.png)]

缺点:造成大量的内存碎片

3. 标记—整理算法

标记-整理是对标记-清除的改进,同样分为两个步骤,只是将清除换成了整理

  • 整理步骤:将被标记的对象全部移动到内存一端,然后将存活对象的后面内存全部释放

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nyT1Kw7a-1691029166511)(https://joshua-1301529810.cos.ap-chengdu.myqcloud.com/img/%E6%A0%87%E8%AE%B0-%E6%95%B4%E7%90%86%E7%AE%97%E6%B3%95.png)]

4.分代收集

分代收集与其说是一种算法,不如说是解决方案。

分代收集将堆内存分为新生代和老年代,针对新生代和老年代的特性采用不同的GC算法进行回收。

新生代:创建和消亡频繁,存活率低,因此采用复制算法

老年代:存活率高,采用标记-清除或者标记-整理

当新生代的对象经过多次GC之后依然存活就会被复制到老年代

三、JVM的做法

JVM采用分代收集的方式进行GC,将堆内存分为了新生代、老年代和持久代,并且将新生代分为Eden、From Survivor、To Survivor三个区间

1. 内存区划分

  • 新生代: Eden + From Survivor(S0) + To Survivor(S1)
  • 老年代
  • 持久代:方法、static变量、JAVA类(1.8之后变为元空间,作用于本地内存,被所有JVM实例共享)

划分Survivor区的好处是防止老年代区被过快填满而发生Full GC,这个过程将会非常慢,如果没有Survivor做缓冲区,每次新生代发生GC都会转化为老年代,老年代很快就满了。

而将Survivor区分为两个区间是为了减少内存碎片,当发生GC的时候,会将Eden和From Survivor的所有存活对象复制到To Survivor,清空Eden和From Survivor,然后将From Survivor和To Survivor角色替换,这也是两个Survivor区大小一样的原因。

2. 内存区的比例

  • 新生代:老年代 = 1:2, 可通过参数-XX:NewRatio配置
  • Eden : S0 : S1 = 8 : 1 : 1, 可通过参数-XX:SurvivorRatio配置
  • 新生代转化为老年代的复制次数为15,可以通过参数-XX:+MaxTenuringThreshold配置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kD1qxk1A-1691029166511)(https://joshua-1301529810.cos.ap-chengdu.myqcloud.com/img/JVM%E5%A0%86%E5%86%85%E5%AD%98%E5%88%86%E4%BB%A3.jpg)]

JVM内存结构

根据《Java虚拟机规范》,JVM的内存数据结构分为以下几个部分
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YdD15XEL-1691029166512)(https://joshua-1301529810.cos.ap-chengdu.myqcloud.com/img/JVM%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.jpg)]

不过我们都知道JVM的实际制定不一定要完全按照规范,在JDK7以前HotSpot虚拟机都是以“永久代”的形式实现方法区,JDK7的时候将运行时常量池(包含类信息、字符串常量池等)移动到了堆内存中,而在JDK8使用了“元空间”完全代替了老年代,但是元空间是放在本地内存中,由多个JVM实例锁共享的,所以这和《Java虚拟机规范》中方法区在JVM实例内部已经不符合了。

1. 方法区

方法区存储了类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
在Hotspot虚拟机中对于该区域的实现发生了多次的变化。

  • JDK6以前:使用永久代实现方法区
  • JDK7:依然使用永久代保存类型信息,但是常量池和静态变量移动到了堆内存
  • JDK8: 使用元空间实现方法区,且元空间存在于本地内存,多个JVM共享,这意味着常量池可以被多个JVM共享,减少了内存的消耗

所以JAVA8中的实际内存结构应该是这样的:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-psyNWBuZ-1691029166512)(https://joshua-1301529810.cos.ap-chengdu.myqcloud.com/img/Java8%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.jpg)]

2. 堆

这个很熟悉了,堆是整个JVM管理的内存中最大的一块,它的唯一目的就是存储对象,Java对象几乎全部都存放在对上,《Java虚拟机规范》中提到所有的对象和数组都应当在对上分配(其实数组也是一个特殊的对象,只不过是由虚拟机操作字节码生成的,格式为[Lxxx.xxx.xxx),之所以说是几乎是因为现在Java确实存在这种手段。

在GC算法的笔记中中也提到过,HotSpot采用分代收集做GC收集,所以JVM堆从逻辑上分为了新生代和老年代(默认比例1:2),其中新生代由由Eden和2个Survivor区组成(默认比例8:1:1)。之所以说是“逻辑上”是因为从G1收集器开始已经出现了不使用分代进行GC的做法,在这种情况下JVM仅保留了逻辑概念上的分代,以便于使用分代回收的收集器依然能够工作。以G1收集器来看,它就是将整个堆分为若干个小块,每次进行GC的时候会根据哪块内存中存放的垃圾最多,将存活的对象复制到新的块中,然后释放掉这一块内存,所以在G1收集器下新生代和老年代都是一部分内存块的集合罢了。

3. 虚拟机栈

每个线程都会拥有其对应的栈,其生命周期是和线程一致的。

虚拟机栈描述的是Java方法执行的线程内存模型:每个方法被执行的时候,JAVA虚拟机都会同步创建一个栈帧用于存储局部变量表、操作数栈、动态连接、方法出口德国信息。每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

4. 本地方法栈

本地方法栈与虚拟机栈功能很相近,区别就是本地方法栈是为Native方法服务,《Java虚拟机规范》中没有对本地方法栈做任何规定,甚至于连我们的HotSpot就直接将本地方法栈和虚拟机栈合二为一了。

5. 程序计数器

这个放在最后主要是因为这部分对于开发者没有直接关系,它只和JVM执行JAVA字节码有关系。我们可以将程序计数器比作字节码的行号指示器,它记录了当前线程执行的是那一条字节码指令,从而可以让线程在切换后恢复到正确的执行位置。

如果线程正在执行的是Java方法,这个计数器记录的就是字节码指令的地址;如果执行的是native方法,这个计数器值就该为Undefined,程序计数器也是《Java虚拟机规范》中唯一一个没有规定OOM的区域。

JVM参数

1.常用JVM参数

  • -Xms:初始堆大小
  • -Xmx:最大堆大小
  • -Xmn:新生代大小
  • -XX:NewRatio:设置新生代和老年代的比值。如:为3,表示年轻代与老年代比值为1:3
  • -XX:SurvivorRatio:新生代中Eden区与两个Survivor区的比值,数值为Eden区占比,默认为8。注意Survivor区有两个。如:为3,表示Eden:Survivor1: Survivor2 =3:1:1,一个Eden区占整个新生代的1/5
  • -XX:MaxTenuringThreshold:设置转入老年代的存活次数。如果是0,则跳过Survivor直接进入老年代
  • -XX:PermSize、-XX:MaxPermSize:分别设置永久代最小大小与最大大小(Java8以前)
  • -XX:MetaspaceSize、-XX:MaxMetaspaceSize:分别设置元空间最小大小与最大大小(Java8以后)

2.收集器设置

  • -XX:+UseSerialGC:使用Serial+SerialOld
  • -XX:+UseParallelGC:使用ParallelScavenge+SerialOld
  • -XX:+UseParalledlOldGC:使用ParallelScavenge+ParallelOld (JDK8默认组合)
  • -XX:+UseConcMarkSweepGC:使用ParNew+(CMS & SerialOld)
  • -XX:+UseG1GC:使用G1 (JDK9之后默认)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/21514.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为云CTS 使用场景

云审计服务 CTS 云审计服务(Cloud Trace Service),帮助您监控并记录华为云账号的活动,包括通过控制台、API、开发者工具对云上产品和服务的访问和使用行为,提供对各种云资源操作记录的收集、存储和查询功能&#xff0…

【Linux 网络】 传输层协议之UDP协议

UDP协议 UDP协议的位置UDP协议的特点UDP协议的格式UDP使用注意事项 UDP协议的位置 在网络套接字编程时用到的各种接口,是位于应用层和传输层之间的一层系统调用接口,这些接口是由系统提供的。我们可以通过这些接口来搭建上层应用,比如HTTP协议…

jenkins pipeline项目

回到目录 将练习jenkins使用pipeline项目,结合k8s发布一个简单的springboot项目 前提:jenkins的环境和k8s环境都已经安装完成,提前准备了gitlab和一个简单的springboot项目 创建一个流水线项目 流水线中选择git,并选择gitlab的…

Linux系统jenkins+newman+postman持续集成环境搭建

1、首先安装nodejs 下载nodejs压缩包,下载地址:nodejs官网下载 建议不用下载最新的,我这里用的是推荐的v12.18版本 下载和解压命令 wget https://nodejs.org/dist/v12.18.3/node-v12.18.3-linux-x64.tar.xz解压安装包(记得没有z&…

rv1109/1126 rknn 模型部署过程

rv1109/1126是瑞芯微出的嵌入式AI芯片,带有npu, 可以用于嵌入式人工智能应用。算法工程师训练出的算法要部署到芯片上,需要经过模型转换和量化,下面记录一下整个过程。 量化环境 模型量化需要安装rk的工具包: rockchip-linux/rk…

【Mybatis】Mybatis架构简介

文章目录 1.整体架构图2. 基础支撑层2.1 类型转换模块2.2 日志模块2.3 反射工具模块2.4 Binding 模块2.5 数据源模块2.6缓存模块2.7 解析器模块2.8 事务管理模块 3. 核心处理层3.1 配置解析3.2 SQL 解析与 scripting 模块3.3 SQL 执行3.4 插件 4. 接口层 1.整体架构图 MyBatis…

爬虫008_流程控制语句_if_if else_elif_for---python工作笔记026

然后我们再来看一下这里的,判断,可以看到 再看一个判断,这里的布尔类型 第二行有4个空格,python的格式 注意这里,输入的age是字符串,需要转一下才行 int可以写到int(intput("阿斯顿法师打发地方")) 这样也可以

成功解决:ValueError Cannot assign non-leaf Tensor to parameter ‘weight‘

成功解决:ValueError Cannot assign non-leaf Tensor to parameter ‘weight‘ 欢迎大家来到安静到无声的《模式识别与人工智能(程序与算法)》,如果对所写内容感兴趣请看模式识别与人工智能(程序与算法)系列讲解 - 总目录,同时这也可以作为大家学习的参考。欢迎订阅,优…

【LeetCode 算法】Reorder List 重排链表

文章目录 Reorder List 重排链表问题描述:分析代码PointerReverseMerge Tag Reorder List 重排链表 问题描述: 给定一个单链表 L 的头节点 head ,单链表 L 表示为: L0 → L1 → … → Ln - 1 → Ln 请将其重新排列后变为&#…

集中/本地转发、AC、AP

1.ADSL ADSL MODEM(ADSL 强制解调器)俗称ADSL猫 ADSL是一种异步传输模式(ATM)。ADSL是指使用电话线上网,需要专用的猫(Modem),在上网的时候高频和低频分离,所以上网电话两不耽误,速…

LBP特征笔记

LBP,局部二值模式(Local Binary Pattern),是一种描述图像局部纹理特征的方式,具有旋转不变性和灰度不变性。首先由T. Ojala, M.Pietikinen, 和 D. Harwood 在1994年提出。 LBP特征描述 基础LBP算子 基础的LBP算子定义…

Mysql推荐使用规范

一:基础规范 1、使用InnoDB存储引擎 支持事务、行级锁、并发性能更好、CPU及内存缓存页优化使得资源利用率更高 2、推荐使用utf8mb4字符集 无需转码,无乱码风险, 支持emoji表情以及部分不常见汉字 3、表、字段必须加注释 方便他人理解字段意思。 4、不在数据…

分布式ID性能评测:CosId VS 美团 Leaf

环境 MacBook Pro (M1)JDK 17JMH 1.36运行在本机 Docker 内的 mariadb:10.6.4 运行 CosId SegmentChainId 模式,基准测试代码: Benchmarkpublic long generate() {return segmentChainId.generate();}Leaf 基准测试代码: Benchmarkpublic l…

使用正则表达式 移除 HTML 标签后得到字符串

需求分析 后台返回的数据是 这样式的 需要讲html 标签替换 high_light_text: "<span stylecolor:red>OPPO</span> <span stylecolor:red>OPPO</span> 白色 01"使用正则表达式 function stripHTMLTags(htmlString) {return htmlString.rep…

stm32 舵机 cubemx

文章目录 前言一、cubemx配置二、代码1.serve.c2.serve.h3.主函数 总结 前言 stm32对舵机进行控制&#xff0c;很简单直接一个pwm就可以实现 pwm的周期是50HZ占空比分别对应 一个0.5ms的高电平对应于0度 一个1.5ms的高电平对应于90度 一个2.5ms的高电平对应于180度 因此&#…

音视频--DTMF信号发送及检测

参考资料 https://zh.wikipedia.org/wiki/%E5%8F%8C%E9%9F%B3%E5%A4%9A%E9%A2%91https://www.cnblogs.com/lijingcheng/p/4454932.html 1. DTMF是什么 1.1 DTMF定义 双音多频信号&#xff08;英语&#xff1a;Dual-Tone Multi-Frequency&#xff0c;简称&#xff1a;DTMF&a…

怎么查到企业的供应商和客户?

企业的供应商和客户是什么&#xff1f; 其实不需要过多介绍&#xff0c;我们对供应商和客户都有自己的理解&#xff0c;供应商就是负责企业产品的供应&#xff0c;企业从供应商那里买材料进行加工得到的产品&#xff0c;卖给客户。 官方来说供应商是向企业和竞争对手提供各种…

Linux进程,线程,内核线程的区别

进程、线程和内核线程是计算机中不同层次的执行单元&#xff0c;它们之间有以下区别&#xff1a; 进程&#xff08;Process&#xff09;&#xff1a;进程是计算机中正在运行的程序的实例。每个进程都有自己的地址空间、代码、数据、打开的文件等资源。进程是操作系统进行资源分…

关于React框架(库)的若干个问题

​一、是什么&#xff1f; ​ react是用于构建用户界面的JavaScript库&#xff0c;是一个将数据渲染为HTML视图的开源JavaScript库。 二、为什么用&#xff1f;有什么好处&#xff08;特点&#xff09;&#xff1f; 1. 为什么用&#xff1f;用的前提&#xff1f; ❶原生js操…

mac 下用brew快速安装CommandLineTools

有时候用git 就会提示安装CommandLineTools &#xff0c;xcode太大又不想安装&#xff0c;怎么办呢我们可以试下下面的方式 什么是Brew&#xff1a; Brew是Mac OS X下的一个包管理器&#xff0c;可以方便地安装、升级和卸载很多常用的软件包 在mac下如何安装呢&#xff1a; …