当前位置: 首页 > news >正文

JVM学习(九):堆

一、堆(Heap)的概述

        一个JVM实例只存在一个堆内存,堆也是Java内存管理的核心区域。

        Java堆区在JVM启动的时候即被创建,其空间大小也就确定了。是JVM管理的最大一块内存空间。同时,堆内存的大小是可以调节的。《Java虚拟机规范》规定,堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的。

        所有的线程共享Java堆,但在堆里还可以划分线程私有的缓冲区 ( Thread Local Allocation Buffer, TLAB)

        《Java虚拟机规范》中对Java堆的描述是:所有的对象实例以及数组都应当在运行时分配在堆上(The heap is the run-time data area from which memory for all class instances and arrays is allocated )。其实从实际使用角度看,“几乎”所有的对象实例都在这里分配内存。数组和对象可能永远不会存储在栈上,因为栈帧中保存引用,这个引用指向对象或者数组在堆中的位置。

        为了说明对象与栈和堆的关系,我们用一段代码:

public class SimpleHeap {//属性、成员变量private int id;public SimpleHeap(int id) {this.id = id;}public void show() {System.out.println("My ID is " + id);}public static void main(String[] args) {SimpleHeap sl = new SimpleHeap(1);SimpleHeap s2 = new SimpleHeap(2);}
}

        在方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才会被移除(因为垃圾收集的时候才会去扫描垃圾)。堆是GC ( Garbage Collection,垃圾收集器)执行垃圾回收的重点区域。

二、堆内存 

2.1 内存细分 

        现代垃圾收集器大部分都基于分代收集理论设计,堆空间细分为:

        Java 7及之前堆内存逻辑上分为三部分:新生区+养老区+永久区

  • Young Generation Space        新生区        Young/New        又被划分为Eden区和survivor区
  • Tenure generation space        养老区         Old/ Tenure
  • Permanent Space                   永久区         Perm

        Java 8及之后堆内存逻辑上分为三部分:新生区+养老区+元空间

  • Young Generation Space        新生区        Young/New        又被划分为Eden区和Survivor区
  • Tenure generation space        养老区         Old/Tenure
  • Meta Space                             元空间         Meta

2.2 常用的相关JVM参数 

2.2.1 堆空间大小设置 

        Java堆区用于存储Java对象实例,堆的大小在JVM启动时就已经设定好了,可以通过选项"-Xmx"和"-Xms"来进行设置。

  • -Xms用于表示堆区的起始内存,等价于-XX:InitialHeapSize
  • -Xmx则用于表示堆区的最大内存,等价于-XX:MaxHeapSize

        一旦堆区中的内存大小超过“-Xmx"所指定的最大内存时,将会抛出OutOfMemoryError异常。

        通常会将 -Xms 和 -Xmx 两个参数配置相同的值,其目的是为了能够在java垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小,从而提高性能

        默认情况下,初始内存大小 = 物理电脑内存大小 / 64;最大内存大小 = 物理电脑内存大小 / 4

        下面来实践一下,写一段代码看看堆内存大小: 

public class HeapSpaceInitial {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");System.out.println("系统内存大小为:" + initialMemory * 64.0 / 1024 + "G");System.out.println("系统内存大小为:" + maxMemory * 4.0 / 1024 + "G");
}

        运行结果:

-Xms : 15M
-Xmx : 247M
系统内存大小为:0.9375G
系统内存大小为:0.96484375G

         修改一下:

-Xms : 580M
-Xmx : 580M
系统内存大小为:36.25G
系统内存大小为:2.265625G

2.2.2 老年代和新生代比例设置

        存储在VM中的Java对象可以被划分为两类:一类是生命周期较短的瞬时对象,这类对象的创建和消亡都非常迅速;另外一类对象的生命周期却非常长,在某些极端的情况下还能够与JVM的生命周期保持一致。

        Java堆区进一步细分的话,可以划分为年轻代(YoungGen)和老年代(OldGen)。其中年轻代又可以划分为 Eden 空间、Survivor0 空间和 Survivor1空间(有时也叫做from区、to区)。

        配置新生代与老年代在堆结构的占比使用 -XX:NewRatio

        默认-XX:NewRatio=2,表示新生代占1,老年代占2,新生代占整个堆的1/3。

2.2.3 年轻代内的比例设置

        在HotSpot中,Eden空间和另外两个survivor空间缺省所占的比例是8:1:1,开发人员可以通过选项 -XX:SurvivorRatio 调整这个空间比例。比如-XX:SurvivorRatio=8。

        几乎所有的Java对象都是在Eden区被new出来的。绝大部分的Java对象的销毁都在新生代进行了。IBM公司的专门研究表明,新生代中80% 的对象都是“朝生夕死”的。

        可以使用选项"-Xmn"设置新生代最大内存大小。这个参数一般使用默认值就可以了。

2.2.4 利用VisualVM直观查看

         我们可以直观地看一下这些比例。随便运行一段不结束的程序:

public class EdenSurvivorTest {public static void main(String[] args) {System.out.println("我只是来打个酱油~");try {Thread.sleep(1000000);} catch (InterruptedException e) {e.printStackTrace();}}
}

         我们设置堆的大小为600M:

        打开visualVM:

 

         我们将堆区设置为600M,默认-XX:NewRatio=2,新生代占1/3,即200M;老年代占2/3,即400M。默认-XX:SurvivorRatio=8,Eden区占8/10,即160M,两个Survivor区各占1/10,即20M。

 2.2.5 关闭自适应的内存分配策略

        JVM默认是开启自适应内存分配策略的,也就是说,新生代的8:1:1 并不会被严格遵守,而是会由虚拟机动态地调整。我们可以使用 -XX:-UseAdaptiveSizePolicy 来关闭自适应的内存分配策略。

三、对象分配过程 

        为新对象分配内存是一件非常严谨和复杂的任务,JVM的设计者们不仅需要考虑内存如何分配、在哪里分配等问题,并且由于内存分配算法与内存回收算法密切相关,所以还需要考虑GC执行完内存回收后是否会在内存空间中等生内存碎片。

3.1 对象分配的一般过程 

        下面我们来看看对象分配的过程: 

        new的对象先放伊甸园区。此区有大小限制,当伊甸园的空间填满时,程序又需要创建对象,此时JVM的垃圾回收器将对伊甸园区进行对象进行垃圾回收(Minor GC),将伊甸园区中的不再被其他对象所引用的对象进行销毁,然后将伊甸园中的剩余对象移动到幸存者0区。幸存者区中会有年龄计数器,每活过一次GC,年龄就会+1。

         如果再次触发垃圾回收,伊甸园区幸存的对象和幸存者0区中依然没有被回收的对象,就会放到幸存者1区。

 

         如果再次经历垃圾回收,此时会重新放回幸存者0区,接着再去幸存者1区。当幸存者区中的对象超过15岁时,就会被分配到老年区。进入老年代的年龄是可以设置的,设置参数: 

-XX:MaxTenuringThreshold=<N> 。

        在养老区,相对悠闲。当养老区内存不足时,再次触发GC(Major GC),进行养老区的内
存清理。

        若养老区执行了Major GC之后发现依然无法进行对象的保存,就会产生OOM异常。

3.2 对象分配的全过程        

 

        这里要注意的是,当new出特别大的对象(Eden放不下)时,首先要进行一次YGC,还是放不下,就直接放到老年代了;如果Servivor区满了,而Eden区没满,此时不会触发YGC。多余的对象会直接放到老年代。 

        GC频繁在新生区收集,很少在养老区收集,几乎不在永久区/元空间收集。  

四、Young GC、Major GC和 Full GC

4.1 概念区分 

        JVM在进行GC时,并非每次都对三个内存(新生代、老年代;方法区)区域一起回收,大部分时候回收的都是新生代。

        针对HotSpot VM的实现,它里面的GC按照回收区域又分为两大种类型:一种是部分收集(Partial GC),一种是整堆收集(Full GC)

  • 部分收集:不是完整收集整个Java堆的垃圾收集。其中又分为:
    • 新生代收集(Minor GC / Young GC):只是新生代的垃圾收集
    • 老年代收集(Major GC / Old GC):只是老年代的垃圾收集。

                        目前,只有CMS GC会有单独收集老年代的行为。

                        注意,很多时候Major GC会和Full GC混淆使用,需要具体分辨是老年代回收还是整堆回收。

  • 混合收集(Mixed GC):收集整个新生代以及部分老年代的垃圾收集。

                目前,只有G1 GC会有这种行为

  • 整堆收集(Full GC):收集整个java堆方法区的垃圾收集

4.2 年轻代Gc(Minor GC)触发机制

        当年轻代空间不足时,就会触发Minor GC,这里的年轻代满指的是Eden满,Survivor满不会引发GC。

        因为Java对象大多都具备朝生夕灭的特性,所以 Minor GC非常频繁,一般回收速度也比较快。Minor GC会引发STW,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行。

4.3 老年代GC (Major GC/Full GC)触发机制

        出现Major GC,经常会伴随至少一次的Minor GC(但非绝对的),也就是在老年代空间不足时,会先尝试触发Minor GC。如果之后空间还不足,则触发Major GC。Major GC的速度一般会比Minor GC慢10倍以上,STW的时间更长。如果Major GC后,内存还不足,就出现OOM。

        触发Full GC执行的情况有如下五种:
(1)调用System.gc()时,系统建议执行Full GC,但是不必然执行

(2)老年代空间不足

(3)方法区空间不足

(4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存

(5)由Eden区、survivor space0 (From Space)区向survivor space1 (ToSpace)区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

        总结起来就一点:老年代或方法区空间不够用了。

相关文章:

JVM学习(九):堆

一、堆&#xff08;Heap&#xff09;的概述 一个JVM实例只存在一个堆内存&#xff0c;堆也是Java内存管理的核心区域。 Java堆区在JVM启动的时候即被创建&#xff0c;其空间大小也就确定了。是JVM管理的最大一块内存空间。同时&#xff0c;堆内存的大小是可以调节的。《Java虚拟…...

golang - switch

switch 的使用 switch 语句用于基于不同条件执行不同操作&#xff0c;&#xff0c;直每一个 case 分支都是唯一的&#xff0c;从上到下逐一测试到匹配为止匹配项后面也不需要再加 break switch 表达式 {case 表达式1, 表达式2, ... :语句块1case 表达式2, 表达式3, ... :语句块…...

浙大数据结构与算法一些有意思的理论基础题

堆栈 有人给出了堆栈用数组实现的另一种方式&#xff0c;即直接在函数参数中传递数组和top变量&#xff08;而不是两者组成的结构指针&#xff09;&#xff0c;其中Push操作函数设计如下。这个Push函数正确吗&#xff1f;为什么&#xff1f; #define MaxSize 100 ElementTyp…...

【热门框架】Mybatis-Plus怎样进行映射匹配兼容?Mybatis-Plus的ID有哪些生成策略

Mybatis-Plus提供了两种映射匹配兼容的方式&#xff1a;驼峰转下划线和全局配置。 驼峰转下划线 默认情况下&#xff0c;Mybatis-Plus会将Java类中的驼峰命名方式自动映射到数据库表中的下划线命名方式。例如&#xff0c;Java类中的userName属性会自动映射到表中的user_name字…...

Http1.0 、1.1、2.0、3.0的区别

巨人的肩膀 3.1 HTTP 常见面试题 | 小林coding HTTP1.0与HTTP1.1 HTTP1.1在HTTP1.0上的改进&#xff1a; 使用长连接的方式改善了HTTP1.0中短连接造成的性能开销支持管道网络传输&#xff0c;不必等到上一个的响应&#xff0c;就可以接着发送第二个请求&#xff0c;减少整体响…...

Python——基于YOLOV8的车牌识别(源码+教程)

目录 一、前言 二 、完成效果 三、 项目包 四、运行项目 &#xff08;教程&#xff09; 一、前言 YOLOv8LPRNet车牌定位与识别https://www.bilibili.com/video/BV1vk4y1E7MZ/ 最近做了有一个车牌识别的小需求&#xff0c;今天完成了&#xff0c;在此记录和分享 首先&#x…...

c# 数据保存为PDF(一) (spire pdf篇)

文章目录 前言了解 Spire使用Spire.PDF1 创建简单的PDF文档2 创建带有格式的PDF文档&#xff08;使用Draw&#xff09;头部信息页眉页脚测试数据完整的代码 3 创建带有格式的PDF文档&#xff08;使用Gird&#xff09;小结 先上一个效果图 前言 项目中需要将一些数据转存为PDF …...

Stable Diffusion使用方法

SD的本地安装教程有很多我就不重复了&#xff0c;这里主要是记录我在使用SD Webui的过程中遇到的问题&#xff0c;总结的一些提升出图效率&#xff0c;出好图概率的经验。 先搞几张看看效果 二次元妹妹 高达 &#xff1f; Ok&#xff0c;以上只是一小部分成品 &#xff0c;属…...

高性能:负载均衡

目录 什么是负载均衡 负载均衡分类 服务端负载均衡 服务端负载均衡——软硬件分类 服务端负载均衡——OSI模型分类 客户端负载均衡 负载均衡常见算法 七层负载均衡做法 DNS解析 反向代理 什么是负载均衡 将用户请求分摊&#xff08;分流&#xff09; 到不同的服务器上…...

Matplotlib 安装介绍

文章目录 安装步骤 Matplotlib 不止是一个数学绘图库&#xff0c;它也是可视化和分析工具中最流行之一。我们可用其制作简单的图表&#xff0c;如折线图和散点图。 安装步骤 先进入&#xff1a;python官网 跳转到界面&#xff1a; 录入并搜索 下载之前&#xff0c;看一下自…...

DNS:关于 DNS 基本概念的一些笔记整理

写在前面 分享一些 DNS 的笔记整理博文内容涉及&#xff1a; DNS 历史介绍DNS 解析顺序DNS 基本概念资源类型介绍DNS 安全 理解不足小伙伴帮忙指正 傍晚时分&#xff0c;你坐在屋檐下&#xff0c;看着天慢慢地黑下去&#xff0c;心里寂寞而凄凉&#xff0c;感到自己的生命被剥夺…...

机器人学一些知识

机器人动力学模型是用数学方法描述机器人运动和力学特性的模型。它包含机器人的几何结构、质量、惯性、摩擦等物理特性&#xff0c;以及机器人的控制系统和传感器等。机器人动力学模型可以用于机器人的运动规划、控制算法设计、仿真和优化等应用中。 机器人动力学模型通常采用…...

应用,auto,内联函数

6.引用&#xff1a; //指针 int main() {int a 0;int& b a;int& c b;int& d c;cout << &a << endl;cout << &b << endl;cout << &c << endl;cout << &d << endl;b;d;cout << a <<…...

Flask框架的学习---01

1.工程搭建&#xff1a; (1) 安装flask: pip3 install flask (2)命令行&#xff1a; &#xff08;1&#xff09;终端运行&#xff1a;flask run &#xff08;2&#xff09;绑定IP地址和端口&#xff1a;Flask run -h 127.0.0.1 -p 8083 修改端口号 &#xff08;3&#xff0…...

免费gpt-4-国内使用gpt-4

如何用上gpt-4 GPT-4尚未正式发布和公开&#xff0c;因此我们无法提供对GPT-4的具体使用方法。但是&#xff0c;可以从GPT-4的前一代——GPT-3的使用经验和GPT-4的预期功能来看&#xff0c;建议如下&#xff1a; 了解GPT-4的语言处理能力和适用场景&#xff1a;GPT-4预计将进一…...

《程序员面试金典(第6版)面试题 16.09. 运算

题目描述 请实现整数数字的乘法、减法和除法运算&#xff0c;运算结果均为整数数字&#xff0c;程序中只允许使用加法运算符和逻辑运算符&#xff0c;允许程序中出现正负常数&#xff0c;不允许使用位运算。 你的实现应该支持如下操作&#xff1a; Operations() 构造函数minus…...

asp.net基于web的校园美食派送配送系统

1&#xff0e;系统登录&#xff1a;系统登录是用户访问系统的路口&#xff0c;设计了系统登录界面&#xff0c;包括用户名、密码和验证码&#xff0c;然后对登录进来的用户判断身份信息&#xff0c;判断是管理员用户还是普通用户。 2&#xff0e;系统用户管理&#xff1a;不管是…...

【JAVA】#详细介绍!!! 文件操作之File对象(1)!

本文内容不涉及文件内容操作&#xff0c;主要是对指定文件元信息的获取&#xff0c;以及通过java代码如何创建一个文件或者删除文件 目录 文件操作的File对象 File对象的基本操作方法 得到文件&#xff08;夹&#xff09;对象的信息元 1.getParent 2. getName 3.getPath 4…...

Vue基本的内置指令

前言 除了常见的v-bind,v-for,v-if,v-on.v-model等&#xff0c;本次学习一些vue提供的其他内置指令 1 v-text 给标签插入文本&#xff0c;类似于插值语法 它会把全部的字符串当成文本去解析,不会当成标签的,哪怕写的是标签结构 效果和插值语法是一样的 插值语法比v-text更加…...

华为孟晚舟当值首秀:2030年AI算力将增长500倍!

作者 | 范智林 来源 | 华商观察 微信号&#xff1a;HuashangGC 孟晚舟当值首次亮相。 4月19日&#xff0c;华为副董事长、轮值董事长、CFO孟晚舟在华为第20届全球分析师大会上进行演讲&#xff0c;这是她当值华为轮值董事长以来的首次公开亮相。 按照华为内部规定&#xff0c…...

关于python异常的总结

Python异常是在程序执行时发生的错误&#xff0c;可能会导致程序终止运行。 在Python中&#xff0c;异常处理是一种机制&#xff0c;它允许开发人员在程序发生异常时捕获、处理和报告这些异常&#xff0c;以便程序可以继续运行或在出现异常时进行优雅的退出。 在Python中&…...

基于Java+SpringBoot+vue学生学习平台详细设计实现

基于JavaSpringBootvue学生学习平台详细设计实现 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式 文章目…...

【云原生网关】Kong 使用详解

目录 一、前言 二、Kong介绍 三、Kong核心组件 3.1 kong组件介绍 3.1.1 Kong Server 3.1.2 Apache Cassandra/PostgreSQL 3.1.3 Kong dashboard 3.2 传统网关与Kong工作模式对比 四、Kong网关特征与架构 4.1 kong网关特征 4.1.1 可扩展性 4.1.2 模块化 4.1.3 在任…...

浅谈之Java多线程

Java多线程是Java语言中一个非常重要的特性&#xff0c;它允许程序同时执行多个任务。通过多线程&#xff0c;程序可以同时处理多项任务&#xff0c;从而缩短程序的执行时间。另外&#xff0c;多线程也有助于利用多核处理器&#xff0c;更好地发挥计算机硬件的性能。 那我们在…...

【Vue3学习笔记1】一个清单应用帮你入门Vue.js

Vue 目前已经是国内最流⾏的前端框架之⼀&#xff0c;Vue 3 带来的诸多优化更是让前端圈迎来了新的潮流&#xff0c;比如&#xff1a; 基于 Proxy 的全新响应式实现&#xff1b; Composition API <script setup> 组织代码的更优方式&#xff1b; 更有料的 TypeScript 支…...

go破冰之旅·8·go函数基本实践及各种玩法

一次5-10分钟即可搞定&#xff0c;以干货效率的学习方式带你更直观的玩转各种玩法&#xff01; 行文不易&#xff0c;一字一句纯手打创造&#xff0c;倾注了不少精力&#xff0c;感谢支持。 目录 什么是函数&#xff1f;有哪些元素&#xff1f; 函数参数、返回值 小程序&…...

Qt - 从零到壹的 打地鼠 游戏

❤️‍&#x1f525;欢迎收看西北风的blog&#xff0c;好男人就是我&#xff0c;我就是西北风。✨ Gitee 地址 W_A_Mole NTC_jason/cc语言 - 码云 - 开源中国 (gitee.com) 目录 &#x1f7e5;一&#xff1a;创建一个主窗体 &#x1f7e3;二.&#xff1a;添加主窗口背景图片…...

代码自动发布系统

之前是jenkins发现gitlab代码更新了就自动获取直接部署到服务器 现在是jenkins自动获取Code之后打包成镜像上传到仓库然后通知docker去拉取更新的镜像 分析 旧∶ 代码发布环境提前准备&#xff0c;以主机为颗粒度静态 新: 代码发布环境多套&#xff0c;以容器为颗粒度编译 …...

qemu-基础篇(一)——安装

文章目录 env安装查看版本查看支持的开发板查看支持的CPU的型号 env ubuntu 安装 sudo apt-get install qemu sudo apt-get install qemu-system-arm sudo apt-get install qemu-system查看版本 qemu-img -V qemu-system-arm --version qemu-system-aarch64 --version返回结…...

从根本上理解Synchronized的加锁过程

作为一个Java开发&#xff0c;对于Synchronized这个关键字并不会陌生&#xff0c;无论是并发编程&#xff0c;还是与面试官对线&#xff0c;Synchronized可以说是必不可少。 在JDK1.6之前&#xff0c;都认为Synchronized是一个非常笨重的锁&#xff0c;就是在之前的《谈谈Java…...