JVM堆内存空间(heap)

news2024/11/13 22:52:42

在Java程序运行时,系统运行过程中产生的大部分实例对象以及数组对象都会被放到堆中存储。

默认情况下,如果不通过参数强制指定堆空间大小,那么JVM会根据当前所在的平台进行自适应调整,起始大小默认为当前物理机器内存的1/64,最大大小默认为当前物理机器内存的1/4。

创建Java堆时,本质上并不是直接在内存中划分了一块完整的空间给JVM,因为在《Java虚拟机规范》中提及到:堆空间在物理上可以是不连续的,只需要逻辑上视为连续即可。所以一个JVM的堆空间在实际的机器内存上,可能是由机器内存中多个不同位置的空间组成的,如下图:
在这里插入图片描述
VM的堆空间结构会根据运行时具体采用的GC收集器来决定。在所有的GC收集器中,大体会将堆空间分为分代、分区两大类:
在这里插入图片描述

JDK 堆不同版本的区别

Java堆同时也是变化比较频繁的区域,在不同Java版本中,堆空间也发生了不同的改变:

● JDK7及之前:堆空间包含新生代、年老代以及永久代。
● JDK8:堆空间包含新生代和年老代,永久代被改为元数据空间,位于堆之外。
● JDK9:堆空间从逻辑上保留了分代的概念,但物理上本身不分代。
● JDK11:堆空间从此以后逻辑和物理上都不分代。

本质上来说,影响堆空间结构的并不是Java版本的不同,Java堆结构是跟JVM运行时所使用的垃圾回收器息息相关的,由GC器决定了运行时的堆空间会被划分为何种结构。

在JDK1.8及之前的Java版本中,几乎所有的GC器都会把堆空间划分为至少两个区域:新生代和年老代,但在JDK1.9到之后的GC器中,大多数的GC器开始了不分代的路子(具体原因稍后分析)。

分代堆空间

分代的含义是指在JVM运行过程中,堆空间是否会被分为不同的区域分别用于存储不同生命周期的对象实例,JDK1.8之前的堆结构是完全分代的,也就是指逻辑+物理上都分代,在运行时物理内存会被划为几块不同的区域,也就是一个Eden区、两个Survivor 区(Form/To区)以及一个Old区,从物理内存上来说各个区域都是完整且连续的内存,每块区域都用于存储不同周期的对象实例,相互之间并不干扰。

不分代堆空间

到了JDK1.9时,G1正式出道,成为了JVM内嵌的默认GC器,Java堆空间从此出现了不分代的概念,但不分代也分为两种情况,一种是逻辑分代,物理不分代,另一种则是逻辑+物理都不分代。

逻辑分代,物理不分代(G1):对象分配的逻辑上还是存在分代的思想,但是物理内存上不会再分为几块完整的分代空间。
逻辑+物理都不分代(ZGC、ShenandoahGC):无论从对象分配的逻辑上还是物理内存上,都不存在分代的概念。

JDK7及之前的堆内存空间划分

在JDK1.7及之前的JVM中,所有的GC器都是物理+逻辑都分代的,包括内嵌的默认GC器Parallel Scavenge(新生代)+ Parallel Old(老年代)也分代,所以一般堆空间会被划分为三个区域:新生代、年老代以及永久代:
● 新生代:一个Eden区、两个Survivor区(Form/To区),比例:8:1:1
● 年老代:一个Old区
● 永久代:方法区
在这里插入图片描述

JDK8堆内存空间划分

到了JDK1.8的时候,JVM将永久代,也就是方法区整合成了元数据空间,并且将其移出了堆,将其放在堆空间外的本地内存中。
在这里插入图片描述

JDK1.8的时候没啥好讲的,和1.7差距不大,最大区别在于移除了方法区,在本地内存中加入了元数据空间来存储之前方法区中的大部分数据(原方法区中的数据并不是所有都被迁移到了元空间存储,有些数据被分散到了JVM各个区域)。除此之外,常量池在1.8的时候也被移到了堆外。

JDK9堆内存空间划分

到了JDK1.9时,堆空间慢慢的开始了划时代的改变,在此之前,堆空间的布局都是采用分代存储的方式,无论从逻辑上还是从物理内存上,都是分代的。但是到了jdk9堆分代根据收集器来使用物理和逻辑分代收集器或逻辑分代收集器

  1. 逻辑分代 (Logical Generations):
    ○ 在逻辑分代中,堆内存被逻辑上划分为不同的区域(如新生代和老年代),但这些区域的实际物理存储并不一定是连续的或固定的。
    ○ 这种分代方式使得垃圾收集器可以根据实际需求动态地调整各个区域的大小和分布。
  2. 物理分代 (Physical Generations):
    ○ 在物理分代中,堆内存被明确地划分为不同的物理区域(如新生代和老年代),这些区域的边界是固定的。
    ○ 这种分代方式使得垃圾收集器更容易管理各个区域,但也限制了动态调整的能力。

在 JDK 9 及之后的版本中,尽管堆内存仍然分为新生代和老年代,但垃圾收集器的实现细节和策略有所不同。以下是一些主要的变化:

1. G1 垃圾收集器

G1 垃圾收集器在 JDK 9 及之后的版本中得到了进一步的改进和完善,也是默认收集器。逻辑分代,G1 的主要特点包括:

● 分区式垃圾收集:G1 将堆内存划分为多个固定大小的 Region,每个 Region 的大小通常是 1MB 到 32MB。
● 并发标记:G1 可以在应用程序运行的同时进行标记操作,减少了应用程序的暂停时间。
● 可控的暂停时间:G1 收集器旨在预测性地控制垃圾收集的暂停时间,使其适合大规模应用。

在 G1 中,新生代和老年代的概念仍然存在,但它们是由多个 Region 动态组成的。例如,一部分 Region 可以被分配给新生代,另一部分 Region 可以被分配给老年代。
在这里插入图片描述

JDK1.9时,G1将Java堆划分为多个大小相等的独立的Region区域,不过在HotSpot的源码TARGET_REGION_NUMBER定义了Region区的数量限制为2048个(实际上允许超过这个值,但是超过这个数量后,堆空间会变的难以管理)。
一般Region区的大小等于堆空间的总大小除以2048,比如目前的堆空间总大小为8GB,就是8192MB/2048=4MB,那么最终每个Region区的大小为4MB
G1保留了年轻代和老年代的概念,但不再是物理隔阂了,它们都是可以不连续物理内存来组成的Region的集合。

G1中的年老代晋升条件和之前的无差,达到年龄阈值的对象会被转入年老代的Region区中,不同的是对于大对象的分配,在G1中不会让大对象进入年老代,在G1中由专门存放大对象的Region区叫做Humongous区,如果在分配对象时,判定出一个对象属于大对象,那么则会直接将其放入Humongous区存储。

Humongous区存在的意义:可以避免一些“短命”的巨型对象直接进入年老代,节约年老代的内存空间,可以有效避免年老代因空间不足时的GC开销。
当堆空间发生全局GC(FullGC)时,除开回收新生代和年老代之外,也会对Humongous区进行回收。

2. 其他垃圾收集器

其他垃圾收集器,如 Serial Collector、Parallel Collector 和 CMS Collector,在 JDK 9 及之后的版本中仍然保留了新生代和老年代的概念。

在 JDK 9 及之后的版本中,JVM 的堆内存仍然保留了分代的概念,分为新生代和老年代。尽管在一些细节上有所变化,但这种分代的概念有助于垃圾收集器更高效地管理内存。

● G1 垃圾收集器:通过分区式垃圾收集、并发标记和可控的暂停时间,使得 G1 成为现代 Java 应用程序中常用的垃圾收集器之一。
● 其他垃圾收集器:仍然保留了新生代和老年代的概念,并在不同场景下具有各自的优势

JDK11的垃圾收集器

默认垃圾回收器G1
jdk11引入的ZGC
在JDK11的时候,Java又推出了一款新的垃圾回收器ZGC,它也是一款基于Region区内存布局的GC器,这款GC器是真正意义上的不分代,无论是从逻辑上还是物理上都不分代。
在这里插入图片描述
在ZGC中,也会把堆空间划分为一个个的Region区域,但ZGC中的Region区不存在分代的概念,它仅仅只是简单的将所有Region区分为了大、中、小三个等级:
● 小型Region区(Small):固定大小为2MB,用于分配小于256KB的对象。
● 中型Region区(Medium):固定大小为32MB,用于分配>=256KB ~ <=4MB的对象。
● 大型Region区(Large):没有固定大小,容量可以动态变化,但是大小必须为2MB的整数倍,专门用于存放>4MB的巨型对象。但值得一提的是:每个Large区只能存放一个大对象,也就代表着你的这个大对象多大,那么这个Large区就为多大,所以一般情况下,Large区的容量要小于Medium区,并且需要注意:Large区的空间是不会被重新分配的

实际上,JDK11中的ZGC并不是因为要抛弃分代理念而不设计分代的堆空间的,因为实际上最开始分代理念被提出的本质原因是源于「大部分对象朝生夕死」这个概念的,而实际上大部分Java程序在运行时都符合这个现象,所以逻辑分代+物理不分代是堆空间最好的结构方案。

堆总结

Java堆空间是JVM运行时内存区域中占比最大的一块,此内存区域唯一的目的就是存储运行时创建出的对象实例。同时,随着运行时采用的GC器不同,Java堆也会被分为不同的结构,其中主要可分为分代和不分代的两类结构。相对来说,分代结构是最适合Java对象“朝生夕死”的特性的,如果堆结构是分代的,可以使得JVM能够更好的管理堆内存中的对象,包括内存的分配以及回收。

有兴趣的同学可以加群
在这里插入图片描述

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

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

相关文章

我的第一个QT程序_创建项目_全程图解

创建项目 1.打开QT开发工具 2.点【创建项目】 在弹出的窗口的项目列表中选择【Application(Qt)】中间列选择 第一项 如上图。 3.输入项目名称和项目保存的路径 4.Build system下拉列表中有三个选项&#xff0c;虽然Qt6主推CMake&#xff0c;所以可以默认&#xff0c;直接下一步…

【用Java学习数据结构系列】震惊,二叉树原来是要这么学习的(二)

看到这句话的时候证明&#xff1a;此刻你我都在努力 加油陌生人 个人主页&#xff1a;Gu Gu Study 专栏&#xff1a;用Java学习数据结构系列 喜欢的一句话&#xff1a; 常常会回顾努力的自己&#xff0c;所以要为自己的努力留下足迹 喜欢的话可以点个赞谢谢了。 作者&#xff…

基于yolov8的玉米病害检测系统python源码+onnx模型+评估指标曲线+精美GUI界面

【算法介绍】 基于YOLOv8的玉米病害检测系统是一款利用前沿深度学习技术开发的智能农业工具。该系统以YOLOv8为核心算法&#xff0c;通过大量玉米病害图片的训练&#xff0c;能够精准识别玉米害虫病害。 该系统具备高效、准确的检测能力&#xff0c;支持图片、批量图片、视频…

7.整数反转

7.整数反转 给你一个 32 位的有符号整数 x &#xff0c;返回将 x 中的数字部分反转后的结果。 如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] &#xff0c;就返回 0。 假设环境不允许存储 64 位整数&#xff08;有符号或无符号&#xff09;。 示例 1&#x…

Linux主机网络参数的设置—IP地址的作用和类型

网络参数管理 一.网络参数 主机名&#xff0c;IP地址&#xff0c;子网掩码&#xff0c;网关&#xff0c;DNS服务器地址 1.配置主机名 hostname命令来查看当前系统的主机名&#xff0c; hosnamectl set-hostname 修改centos7的主机名&#xff0c; 建议以FQDN的&#xff…

顶级 USB 恢复工具探讨:2024 -2025 年最佳 USB 数据恢复

在数字数据与物理文档一样重要的时代&#xff0c;丢失 USB 驱动器中的数据可能是一种令人恐慌的经历。无论是重要的工作文件、珍贵的照片还是重要文档&#xff0c;数据丢失都会以难以想象的方式让您倒退。值得庆幸的是&#xff0c;一些 USB 恢复工具旨在帮助您度过这些可怕的时…

【Delphi】一种生成透明 Icon 图标方法、原理

在程序开发中&#xff0c;我们会遇到制作程序的主图标&#xff0c;windows下程序的图标给是要求是ico格式&#xff0c;也就是常说的Icon。本文介绍一种Delphi利用windos API生成icon的方法。 在制作ico图标的时候&#xff0c;我们需要两幅bitmap图片&#xff0c;一幅我们称作掩…

单片机中的定时器:精确时间的掌控者

在单片机的世界里&#xff0c;定时器就像是一个精确的时间守护者&#xff0c;默默地为各种任务提供准确的时间基准。从简单的定时功能到复杂的实时控制系统&#xff0c;定时器都发挥着至关重要的作用。本文将深入探讨单片机中的定时器&#xff0c;包括其工作原理、应用场景以及…

华为OD机试真题 - 高效货运 - 暴力搜索(Java/Python/JS/C/C++ 2024 E卷 100分)

华为OD机试 2024E卷题库疯狂收录中,刷题点这里 专栏导读 本专栏收录于《华为OD机试真题(Java/Python/JS/C/C++)》。 刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX…

JAVA开源项目 加油站管理系统 计算机毕业设计

本文项目编号 T 003 &#xff0c;文末自助获取源码 \color{red}{T003&#xff0c;文末自助获取源码} T003&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

CTFHub技能树-备份文件下载-网站源码

目录 法一&#xff1a;使用自动化工具扫描 方法二&#xff1a;使用dirsearch目录扫描器扫描 法一&#xff1a;使用自动化工具扫描 import requestsurl1 http://challenge-0e8fe706d11de65e.sandbox.ctfhub.com:10800/ # url为被扫描地址&#xff0c;后不加‘/’# 常见的网…

【论文分享】sIOPMP: Scalable and Efficient I/O Protection for TEEs 24‘ASPLOS

目录 AbstractINTRODUCTIONBACKGROUND and MOTIVATIONDMA AttackIOPMPRelated Work: Other I/O Isolation Mechanisms DESIGN OVERVIEWDesign GoalsPerformanceSecurityScalability Threat ModelPrivilege software attacksMalicious device attacks DETAILED DESIGNMulti-stag…

【C++】智能指针——auto_ptr,unique_ptr,shared_ptr

目录 auto_ptr unique_ptr shared_ptr 并发问题 循环引用问题 个人主页&#xff1a;传送门——>东洛的克莱斯韦克 智能指针的原理&#xff1a;传送门——>智能指针的原理 auto_ptr 使用方法参考官方文档 传送门——>auto_ptr文档 auto_ptr并不是一个优秀的智能…

在线将多张图片拼接起来图工具HTML源码

源码介绍 在线将多张图片拼接成一张图片&#xff0c;多图合一并导出下载。无需本地安装软件。 下载时&#xff0c;使用日期时间作为文件名&#xff0c;规避图片文件名相同造成的覆盖问题&#xff1b;也能省去一部覆盖确认操作 多语言支持 源码截图 源码下载 在线将多张图片…

Xilinx实现对数运算

简介 本章节实现任意底数和真数值的转换,设计中一般有两种实现方法: 1、在外部直接算好对数值,按照数值范围做个表,存在ram里,到时候查表。为了减少表深度,提高资源利用率,可以考虑去掉部分低位数值,损失一定的精度。 2、log10(x)=ln(x) * log10(e) , log10(e)是常数可…

电信500M宽带+AX210无线网卡测速

500M电信宽带&#xff0c;PC的Wifi模块是AX210 一、PC测速 2.4G Wifi 5G Wifi 有线网口 二、 手机端&#xff0c;小翼管家App测速 2.4G Wifi 5G Wifi 结论&#xff1a; 手机上网要快的话&#xff0c;还是要选择5G wifi

【Linux】用户和权限及实用操作------迅速了解用户和权限及其实用操作

目录 &#x1f354; Linux用户和权限 1.1 Linux 用户相关概念 1.2 用户权限 1.3 文件/文件夹权限的修改 &#x1f354; Linux实用操作 2.1 快捷键 2.2 软件安装/服务启动状态管理/创建软连接 yum install systemctl 对服务进行管理 ln 软连接 2.3 IP 和 主机名 2.4…

华为云征文 | 快速部署华为云Flexus X实例,开启您的云端之旅

需要了解 本文章主要讲述华为云Flexus X实例的介绍&#xff0c;以及在华为公有云平台&#xff0c;购买和配置华为云Flexus X实例的搭建指南选择合适的云服务器&#xff1a; 本文采用的是 华为云服务器 Flexus X 实例&#xff08;推荐使用&#xff09;共有有镜像&#xff1a; Hu…

关于C++的一些使用模版-初阶

一、泛型编程 如何实现一个通用的交换函数呢?,交换的值是两个类型不同的数据。 代码如下&#xff1a; #define _CRT_SECURE_NO_WARNINGS 1 #include<iostream>//如何实现一个通用的交换函数呢&#xff1f; void swap(int& left, int &right) {int tmp lef…

【拉取Git项目到本地,知识小记,后续再改】

前提&#xff1a;Git已经安装好 https://blog.csdn.net/mukes/article/details/115693833 安装至步骤2.2.4即可 第一步创建本地项目目录 第二步获取他人提供的项目git地址或者自己在网上找的他人项目的git地址 Git 全局设置: git init git config --global user.name “ASxx”…