jvm - GC篇

如何减慢一个对象进入老年代的速度,如何降低GC的次数

堆内存细分

年轻代(Young Generation):

新创建的对象首先被分配在年轻代中。年轻代又被进一步划分为一个Eden区和两个Survivor区(通常称为S0和S1)。
当Eden区满时,会触发一次Minor GC(垃圾回收),存活的对象会被移动到一个Survivor区,不存活的对象会被清理。

老年代(Old Generation 或 Tenured Generation)

  • 经过多次GC后仍存活的对象会被移动到老年代。老年代的空间通常比年轻代大,因为它存储的是生命周期较长的对象。
  • 老年代的垃圾回收频率通常低于年轻代,但每次GC耗时更长,因为涉及到更多的对象和更大的内存区域。

永久代(Permanent Generation)或元空间(Metaspace)

  • 在早期版本的JVM中,永久代用于存储JVM内部结构,如类的元数据、方法的字节码等。从Java 8开始,永久代被元空间替代。
  • 元空间不在堆内存中,而是直接使用本地内存(即操作系统的内存),主要用于存储类的元数据。

大对象区(Large Object Space,或称为Huge Object Space)

  • 一些JVM实现可能会为大对象提供专门的内存区域。这些对象由于大小超过了某个阈值,直接在老年代或特定的大对象区进行分配,以避免在年轻代中频繁复制。

空间大小

Eden与Survivor空间的比例可能接近8:1,也就是说,当每个Survivor空间占用10%的新生代空间时,Eden空间占用80%的新生代空间。新生代的总大小通常是堆内存的1/3到1/4,但这也取决于具体的JVM配置和可用内存。Survivor空间虽然有两个区域,但总有一个区域是空,也不会对其计算大小

老年代通常占据堆内存的其余部分。如果新生代占用了堆的1/3,那么老年代则大约占用2/3。

元空间并不在堆内存中,而是使用本地内存(native memory),用于存储类元数据。
在JDK 8及之后的版本中,默认情况下,元空间是没有硬性限制的(即没有默认的最大值),它会根据需要扩展,直到受限于系统内存。

垃圾收集的流程

常规情况

  • 对象最初分配在Eden区。随着应用程序的运行,Eden区会逐渐填满。当我们Eden区满了后,就会触发GC操作,一般被称为 YGC / Minor GC操作,将伊甸园区中的不再被其他对象所引用的对象进行销毁。再加载新的对象放到伊甸园区。
  • 存活的对象会被移动到一个幸存者from区
  • 随着应用程序的运行,Eden区会再次填满,执行Minor GC操作,将伊甸园区中的不再被其他对象所引用的对象进行销毁,伊甸园区依然存活的对象存放到幸存者to区,同时幸存者from区的对象也复制到幸存者to区,经过一次回收后还存在的对象,将其年龄加 1。如此循环往复,当Survivor中的对象的年龄达到15的时候,将会触发一次 Promotion 晋升的操作,也就是将年轻代中的对象晋升到老年代中,在对伊甸园区做GC的时候,幸存者区满了,幸存对象会被直接提升到老年代(Old Generation)。这意味着即使对象的年龄没有达到通常提升的阈值,它们也会被移动到老年代,因为没有足够的空间在幸存者区容纳它们
  • 如果老年代也没有足够的空间来容纳这些被提升的对象,JVM可能会触发一个完整的垃圾收集(Full GC),这是一个更彻底的收集过程,涉及整个堆,包括年轻代和老年代。Full GC通常比仅针对年轻代的Minor GC要慢得多,因为它需要检查和清理整个堆空间。
  • 内存不足错误(OutOfMemoryError):如果老年代也已满,并且无法为对象提供更多空间,JVM将无法继续运行,并抛出java.lang.OutOfMemoryError。这通常是一个严重的错误,表明应用程序需要更多内存,或者有内存泄漏需要修复

存在大对象的情况

对象过大,Eden园区放不下了,此时要先对Eden园区做一下垃圾回收,如果还是放不下,说明这是一个大对象,可以直接往老年代去放,老年代还是放不下,进行FULL GC,还是放不下,报OOM,重复过程:这个过程会随着Eden区的再次填满而重复,Survivor From区和Survivor To区会在每次Minor GC后交换角色。

from/to区,翻转的目的/好处是什么?

  • 内存回收:垃圾收集的主要任务是识别并回收不再使用的对象所占用的内存。通过翻转,可以清空整个伊甸园区(Eden Space)和一个幸存者区(“From”),这样能快速回收大量内存。
  • 减少碎片:通过将存活对象复制到一个连续的内存区域("To"区),可以避免内存碎片的产生。这样,存活对象在内存中保持紧凑排列,而不是散布在内存的各个角落。
  • 方便计数:在这个过程中,对象的年龄也被跟踪。每次对象在幸存者区之间移动时,它们的年龄就会增加

命令

-Xms:设置堆空间大小初始内存大小(年轻代 + 老年代),默认是服务器可用物理内存的1/64,但服务器可用物理内存比物理内存值要小,因为操作系统自身会占用一部分内存。所以想要一个精确值需要手动去指定,比如-Xms600m,最终实际值大约是575,因为俺默认大小来算,幸存者区占1/24,始终有一个是空的
-Xmx:设置堆空间大小最大内存大小(年轻代 + 老年代),默认是服务器物理内存的1/4
-XX:Survivor:设置幸存者区在新生代的比例,默认是8
-XX:NewRatio:设置新生代和老年代的比例,默认是2,即新生代占总内存的1/3
-XX:printGCDetail

jinfo -flag NewRatio

public static void main(string[] args){
	//返回Java虚拟机中的堆内存总量
	long initialMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024;
	//返回Java虚拟机试图使用的最大堆内存量
	long maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024;
	System.out.println("-Xms : " + initialMemory + "M");
	System.out.println("-Xmx : " + maxMemory + "M");
}

开发环境设置,建议堆空间大小初始内存大小和堆空间大小最大内存大小相等,避免频繁的扩容与释放

堆的细分

开发中,不同的对象生命周期不同,有的对象转瞬即逝,有的对象甚至伴随整个jvm的运行周期

  • 转瞬即逝,比如局部变量,方法执行完毕对应的引用出栈,后续会被回收
  • 生命周期非常长的对象,比如静态变量,集合,例如我们比较熟悉的Spring使用一级缓存来存储单实例类型的bean,这个一级缓存本质上也是一个静态的map,此外还有各种池类资源比如线程池,链接池,他们的生命周期都很长,往往伴随整个jvm的运行周期

生命周期非常长的对象,没有必要对他们持续的做GC,所以当一个对象年龄超过15的时候,jvm认为这是一个稳定的对象,晋升老年代,老年代的GC频率是比较低的

伊甸园区

-XX:Survivor:设置幸存者区在新生代的比例,默认是8,但有一个自适应机制,所以需要显示声明这个值为8,才能获得正确的比例

-XX maxTenuringThreshold:

-Xmn:设置新生代大小

超过了80%对象都是朝生夕死的

几户所有对象都是在伊甸园区创建的,除非是

如何使对象更快的从新生代晋升老年代

在JVM的垃圾回收机制中,对象的晋升主要是通过两种方式实现的:

通过设置对象的年龄阈值。当对象在新生代中经历了一定次数的垃圾回收后,就会被晋升到老年代中。这个次数可以通过-XX:MaxTenuringThreshold参数设置。默认情况下,这个值是15。如果你想让对象更快的晋升到老年代,你可以降低这个值。

通过设置新生代的大小。如果新生代的空间不足以容纳所有的存活对象时,那么还没有达到年龄阈值的对象也会被晋升到老年代。因此,你也可以通过减小新生代的大小来加速对象的晋升。这个可以通过-XX:NewSize参数设置。

以上两种方式都可以使对象更快的从新生代晋升到老年代,但是也需要注意,过快的晋升可能会导致老年代的空间占用增加,从而影响到垃圾回收的效率。所以,在设置这些参数时,需要根据实际的应用情况进行权衡。

可达性分析算法

可达性分析算法的基本思路是通过一系列的称为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到"GC Roots"没有任何引用链相连时,则证明此对象是不可用的,可作为GC Roots引用链的对象

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

被堆中某个实例所引用的对象会被垃圾回收吗
如果只是被堆中某个实例锁引用,那要看这个引用它的实例对象是否由GC ROOT的起始点可达,如果不可达,如果说明这只是实例对象之间的引用,那么这些实例对象在下次GC中都是会被作为垃圾被回收的

栈中的引用被回收,它所引用的对象会被垃圾回收吗
要看情况,当这个实例对象栈中的引用被回收时,要看下这个对象是否还能GC ROOT的起始点可达,比如局部变量,栈中的引用被回收时,那就是不可达,下次GC被垃圾回收,如果是静态变量,栈中的引用被回收时,这个对象依然还能通过GC ROOT的起始点可达

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

吊打同类软件免费又可批量使用

聊一聊 对于经常用到席卡的人来说,每次打印都觉得麻烦,要是有个软件,直接输入名称就能打印就好了。 这不,只要你想,就肯定能实现;如果没实现,就说明你不够想。 这个软件我测试了下&#xff0…

2.攻防世界PHP2及知识点

进入题目页面如下 意思是你能访问这个网站吗? ctrlu、F12查看源码,什么都没有发现 用kali中的dirsearch扫描根目录 命令如下,根据题目提示以及需要查看源码,扫描以php、phps、html为后缀的文件 dirsearch -u http://61.147.17…

网络工程师 (11)软件生命周期与开发模型

一、软件生命周期 前言 软件生命周期,也称为软件开发周期或软件开发生命周期,是指从软件项目的启动到软件不再被使用为止的整个期间。这个过程可以细分为多个阶段,每个阶段都有其特定的目标、任务和产出物。 1. 问题定义与需求分析 问题定义…

深度学习练手小例子——cifar10数据集分类问题

CIFAR-10 是一个经典的计算机视觉数据集,广泛用于图像分类任务。它包含 10 个类别的 60,000 张彩色图像,每张图像的大小是 32x32 像素。数据集被分为 50,000 张训练图像和 10,000 张测试图像。每个类别包含 6,000 张图像,具体类别包括&#x…

力扣257. 二叉树的所有路径(遍历思想解决)

Problem: 257. 二叉树的所有路径 文章目录 题目描述思路复杂度Code 题目描述 思路 遍历思想(利用二叉树的先序遍历) 利用先序遍历的思想,我门用一个List变量path记录当前先序遍历的节点,当遍历到根节点时,将其添加到另一个List变量res中&…

力扣第149场双周赛

文章目录 题目总览题目详解找到字符串中合法的相邻数字重新安排会议得到最多空余时间I 第149场双周赛 题目总览 找到字符串中合法的相邻数字 重新安排会议得到最多空余时间I 重新安排会议得到最多空余时间II 变成好标题的最少代价 题目详解 找到字符串中合法的相邻数字 思…

算法题(54):插入区间

审题: 需要我们把newinterval的区间与interval的区间合并起来,并返回合并后的二维数组地址 思路: 方法一:排序合并区间 我们可以先把newinterval插入到interval中,进行排序然后复用合并区间的代码 方法二:模…

网工_HDLC协议

2025.01.25:网工老姜学习笔记 第9节 HDLC协议 9.1 HDLC高级数据链路控制9.2 HDLC帧格式(*控制字段)9.2.1 信息帧(承载用户数据,0开头)9.2.2 监督帧(帮助信息可靠传输,10开头&#xf…

[免费]微信小程序智能商城系统(uniapp+Springboot后端+vue管理端)【论文+源码+SQL脚本】

大家好,我是java1234_小锋老师,看到一个不错的微信小程序智能商城系统(uniappSpringboot后端vue管理端),分享下哈。 项目视频演示 【免费】微信小程序智能商城系统(uniappSpringboot后端vue管理端) Java毕业设计_哔哩哔哩_bilibili 项目介绍…

nth_element函数——C++快速选择函数

目录 1. 函数原型 2. 功能描述 3. 算法原理 4. 时间复杂度 5. 空间复杂度 6. 使用示例 8. 注意事项 9. 自定义比较函数 11. 总结 nth_element 是 C 标准库中提供的一个算法&#xff0c;位于 <algorithm> 头文件中&#xff0c;用于部分排序序列。它的主要功能是将…

CF 581A.Vasya the Hipster(Java实现)

题目分析 红色袜子数量a&#xff0c;蓝色袜子数量b&#xff0c;题目是个潮哥儿&#xff0c;首先选择两种袜子混搭&#xff0c;搭不出来就纯色 思路分析 混搭数量取决于最小数量&#xff0c;剩余的纯色数量取决于哪个还有剩余且数量要/2 代码 import java.util.*;public class…

C基础寒假练习(6)

一、终端输入行数&#xff0c;打印倒金字塔 #include <stdio.h> int main() {int rows;printf("请输入倒金字塔的行数: ");scanf("%d", &rows);for (int i rows; i > 0; i--) {// 打印空格for (int j 0; j < rows - i; j) {printf(&qu…

Python在线编辑器

from flask import Flask, render_template, request, jsonify import sys from io import StringIO import contextlib import subprocess import importlib import threading import time import ast import reapp Flask(__name__)RESTRICTED_PACKAGES {tkinter: 抱歉&…

ASP.NET Core 中间件

目录 一、常见的内置中间件 二、自定义中间件 三、中间件的执行顺序 四、其他自动逸中间件案例 1. 身份验证中间件 2、跨域中间件&#xff08;CORS&#xff09; ASP.NET Core 中&#xff0c;中间件&#xff08;Middleware&#xff09;是处理 HTTP 请求和响应的组件链。你…

LevelDB 源码阅读:写入键值的工程实现和优化细节

读、写键值是 KV 数据库中最重要的两个操作&#xff0c;LevelDB 中提供了一个 Put 接口&#xff0c;用于写入键值对。使用方法很简单&#xff1a; leveldb::Status status leveldb::DB::Open(options, "./db", &db); status db->Put(leveldb::WriteOptions…

2007-2019年各省科学技术支出数据

2007-2019年各省科学技术支出数据 1、时间&#xff1a;2007-2019年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标&#xff1a;行政区划代码、地区名称、年份、科学技术支出 4、范围&#xff1a;31省 5、指标解释&#xff1a;科学技术支出是指为促进科学研究、技术开发…

2025年1月22日(网络编程 udp)

系统信息&#xff1a; ubuntu 16.04LTS Raspberry Pi Zero 2W 系统版本&#xff1a; 2024-10-22-raspios-bullseye-armhf Python 版本&#xff1a;Python 3.9.2 已安装 pip3 支持拍摄 1080p 30 (1092*1080), 720p 60 (1280*720), 60/90 (640*480) 已安装 vim 已安装 git 学习…

如何对系统调用进行扩展?

扩展系统调用是操作系统开发中的一个重要任务。系统调用是用户程序与操作系统内核之间的接口,允许用户程序执行内核级操作(如文件操作、进程管理、内存管理等)。扩展系统调用通常包括以下几个步骤: 一、定义新系统调用 扩展系统调用首先需要定义新的系统调用的功能。系统…

当卷积神经网络遇上AI编译器:TVM自动调优深度解析

从铜线到指令&#xff1a;硬件如何"消化"卷积 在深度学习的世界里&#xff0c;卷积层就像人体中的毛细血管——数量庞大且至关重要。但鲜有人知&#xff0c;一个简单的3x3卷积在CPU上的执行路径&#xff0c;堪比北京地铁线路图般复杂。 卷积的数学本质 对于输入张…

深度学习的应用

目录 一、机器视觉 1.1 应用场景 1.2 常见的计算机视觉任务 1.2.1 图像分类 1.2.2 目标检测 1.2.3 图像分割 二、自然语言处理 三、推荐系统 3.1 常用的推荐系统算法实现方案 四、图像分类实验补充 4.1 CIFAR-100 数据集实验 实验代码 4.2 CIFAR-10 实验代码 深…