JVM运行时数据区-堆

目录

一、堆的核心概述

(一)概述

(二)堆空间细分

(三)jvisualvm工具

二、设置堆内存的大小与OOM

三、年轻代与老年代

四、图解对象分配一般过程

五、对象分配特殊过程

六、常用调优工具

七、MinorGC,MajorGC,FullGC

(一)MinorGC的触发条件

(二)老年代GC(MajorGC/FullGC)触发条件

(三)FullGC的触发条件

八、内存分配策略

九、为对象分配内存TLAB

十、小结堆空间的参数设置

十一、堆是分配对象的唯一选择吗

逃逸分析

1、栈上分配

2、同步策略

3、分离对象或标量替换


这次学习的是JVM运行时数据区中的堆内存

一、堆的核心概述

(一)概述

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

2、Java堆区在JVM启动的时候即被创建,其空间大小也就确认了。堆内存的大小是可调节的(可以通过参数调节堆的最大内存和初始内存等)

3、Java虚拟机规范规定,堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的(这里涉及虚拟内存的概念)。

4、所有的线程共享Java堆,在这里还可以划分线程私有的缓冲区(TLAB)

5、“几乎”所有的对象实例都在堆上分配内存(为什么是“几乎”,后面会讲到逃逸分析)

6、数组和对象可能永远不会存储在栈上,因为栈帧中保存引用,引用指向对象或者数组在堆中的位置(为什么是“可能”,逃逸分析可能直接在栈上为对象分配空间,而不用在堆上开辟空间了)

7、方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才会被移除。堆是GC执行垃圾回收的重点区域

(二)堆空间细分

Java7及之前内存逻辑上分为:

  • 新生区
    • Eden区
    • Survivor区
      • from
      • to
        • 谁空谁是to
  • 养老区
  • 永久区

Java8及之后内存逻辑上分为:

  • 新生区
    • Eden区
    • Survivor区
      • from
      • to
        • 谁空谁是to
  • 养老区
  • 元空间

不同书籍叫法不一样,看到这些名词知道是同个意思就可以了

  • 新生区==新生代==年轻代
  • 养老区==老年区==老年代
  • 永久区==永久代

(三)jvisualvm工具

可以通过cmd命令行jvisualvm开启,然后在菜单栏工具中安装插件后可以查看GC情况

二、设置堆内存的大小与OOM

1、-XX:+PrintGCDetails:可开启打印查看方法区实现

2、-Xms :小秘书表示堆空间的起始内存。

3、-Xmx:小明星表示堆空间的最大内存        超过最大内存将抛出OOM

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

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

4、cmd命令

jps命令         查看当前程序运行的进程

jstat        查看JVM在gc时的统计信息        jstat -gc 进程号

三、年轻代与老年代

Java对象划分为两类:生命周期短和长的。

新生代与老年代空间默认比例1:2,可以通过设置参数来控制

  • -XX:NewRatio=2,表示新生代占1,老年代占2,新生代占整个堆的1/3
    • ratio:比率比例的意思

jinfo -flag 关键词 进程号,查看参数设定值

  • 在HotSpot中,Eden空间和另外两个Survivor空间缺省所占的比例是:8:1:1
    • -XX:SurvivorRatio调整这个空间比例
  • Eden与Survivor区的比例
    • 实际是6:1:1,因为有自适应机制
      • -XX:-UseAdaptiveSizePolicy:-表示关闭自适应,实际没有用。直接用Ratio分配即可

几乎所有的Java对象都是在Eden区被new出来的。

Eden放不了的大对象,直接进入老年代了。

IBM研究表明,新生代80%的对象都是朝生夕死

-Xmn:洗面奶,设置新生代最大内存大小,如果同时设置了新生代比例与此参数冲突,则以此参数为准。

hotspot为什么要分为新生代和老年代?

因为分代的唯一理由就是优化GC的性能。大多数的对象都是朝生夕死,如果没有进行分代,那所有的对象都在一块,GC的时候要找到哪些对象可以回收就需要对所有对象都进行扫描,性能开销较大。而如果分代的话,新创建的对象放到某一地方,当GC的时候先把这块存储“朝生夕死”对象的区域进行回收,这样就能够以较高的效率释放空间。

设置比例不同可能导致的结果

1)新生代设置过小

    一是新生代GC次数非常频繁,增大系统消耗;二是导致大对象直接进入旧生代,占据了旧生代剩余空间,诱发Full GC

2)新生代设置过大

    一是新生代设置过大会导致老年代过小(堆总量一定),从而诱发Full GC;二是新生代GC耗时大幅度增加

    一般说来新生代占整个堆1/3比较合适

3)Survivor设置过小

    导致对象从eden直接到达旧生代,降低了在新生代的存活时间

4)Survivor设置过大

    导致eden过小,增加了GC频率

    另外,通过-XX:MaxTenuringThreshold=n来控制新生代存活时间,尽量让对象在新生代被回收

四、图解对象分配一般过程

1、new的对象先放在Eden区,此区有大小限制

2、当创建新对象,Eden空间填满时,会触发Minor GC,将Eden不再被其他对象引用的对象进行销毁。再加载新的对象放到Eden区

3、将Eden中剩余的对象移到幸存者0区

4、再次触发垃圾回收,此时上次幸存者下来的,放在幸存者0区的,如果没有回收,就会放到幸存者1区

5、再次经历垃圾回收,又会将幸存者重新放回幸存者0区,依次类推

6、可以设置一个次数,默认是15次,超过15次,则会将幸存者区幸存下来的转去老年区

-XX:MaxTenuringThreshold=N进行设置

总结:

针对幸存者s0,s1区的总结:复制之后有交换,谁空谁是to

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

五、对象分配特殊过程

触发YGC,幸存者区就会进行回收,不会主动进行回收

超大对象eden放不下,就要看Old区大小是否可以放下。如果Old区放得下,就直接放到Old区。如果也放不下,需要FullGC(MajorGC),这两GC概念还是有区别的。下面详解

六、常用调优工具

  1. JDK命令行
  2. Eclipse:Memory Analyzer Tool
  3. Jconsole
  4. VisualVM
  5. Jprofiler
  6. Java Flight Recorder

七、MinorGC,MajorGC,FullGC

针对 HotSpot VM 的实现,它里面的 GC 其实准确分类只有两大种:

部分收集 (Partial GC):

  • 新生代收集(Minor GC / Young GC):只对新生代进行垃圾收集;
  • 老年代收集(Major GC / Old GC):只对老年代进行垃圾收集。需要注意的是 Major GC 在有的语境中也用于指代整堆收集;
  • 混合收集(Mixed GC):对整个新生代和部分老年代进行垃圾收集。

整堆收集 (Full GC):收集整个 Java 堆和方法区。

(一)MinorGC的触发条件

当年轻代空间不足时,就会触发MinorGC,这里的年轻代指的是Eden满,Survivor满不会触发GC。每次MinorGC会清理年轻代的内存

因为Java对象大多朝生夕灭,所以MinorGC非常频繁。MinorGC会引发STW(stop the world即停止用户线程,让GC线程运行)

(二)老年代GC(MajorGC/FullGC)触发条件

指发生在老年代的GC,对象从老年代消失,我们说“MajorGC”“FullGC”发生了

出现了MajorGC,经常会伴随至少一次MinorGC

但是并非绝对,在Parallel Scavenge收集器的收集策略里就直接进行MajorGC的策略选择过程,也就是老年代空间不足,会先尝试触发MinorGC,如果之后空间还不足,则触发MajorGC

MajorGC的速度比MinorGC慢10倍以上,STW的时间更长

如果MajorGC后,内存还不足,就报OOM了

(三)FullGC的触发条件

1、调用System.gc()时,系统建议执行FullGC,但是不必然执行

2、老年代空间不足

3、方法区空间不足

4、通过MinorGC后进入老年代的平均大小,大于老年代的可用内存

5、由Eden区,Survivor 0区向Survivor 1区复制时,对象的大小大于ToSpace可用内存,则把改对象转存到老年代,且老年代的可用内存小于该对象的大小

FullGC是开发或调优中尽量要避免的,这样暂停时间会短一些。

八、内存分配策略

1、如果对象再Eden出生并经过第一次MinorGC后仍然存活,并且能被Survivor区容纳,则被移动到Survivor空间中,并将对象年龄设置为1,对象再Survivor区每熬过一次MinorGC,年龄就+1,当年龄增加到一定程度(默认为15,不同Jvm,GC都所有不同)时,就会被晋升到老年代中

可以通过参数 -XX:MaxTenuringThreshold 设置阈值

2、如果大对象Eden放不下,会直接分配到老年代

我们应尽量避免程序中出现过多的大对象

3、动态对象年龄分配

如果Survivor区中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代,无需等到MaxTenuringThreshold中要求的年龄

4、空间分配担保机制

  • 在发生Minor GC之前,虚拟机会检查老年代最大可用的连续空间,是否大于新生代所有对象的总空间
    • 如果大于,则此次MinorGC是安全的
    • 如果小于,则查看-XX:HandlePromotionFailure设置是否允许担保失败
      • true
        • 会继续检查老年代最大可用连续空间是否大于历次晋升到老年代的对象的平均大小
        • 大于,则尝试进行一次MinorGC,但是这次MinorGC依然是有风险的
        • 小于,则改为进行一次FullGC
      • false
        • 则改为进行一次FullGC
  • jdk6update24之后,这个参数不会再影响到虚拟机的空间分配担保策略。规则改为只要老年代的连续空间大于新生代对象总大小,或者历次晋升的平均大小,就会进行MinorGC;否则进行FullGC

通过 -XX:HandlePromotionFailure=true/false 进行设置是否允许担保失败 

九、为对象分配内存TLAB

堆区是线程共享区域,任何线程都可以访问到堆区的共享数据。由于对象实例的创建在JVM中非常频繁,因此在并发环境下从堆区中划分内存空间是线程不安全的。为避免多个线程操作同一地址,需要使用加锁等机制,进而影响分配速度。

于是TLAB(Thread Local Allocation Buffer)出现了

从内存模型而不是垃圾收集的角度,对Eden区域进行划分,JVM为每个线程分配了一个私有缓存区域,包含在Eden空间中

多线程同时分配内存时,使用TLAB可以避免一系列的非线程安全问题,同时还能够提升内存分配的吞吐量,因此我们将这种内存分配方式称为快速分配策略

1、尽管不是所有的对象实例都能够在TLAB中成功分配内存,但是JVM确实是将TLAB作为内存分配的首选

2、开发人员通过-XX:UseTLAB设置是否开启TLAB空间

3、默认情况下,TLAB空间内存非常小,仅占有整个Eden空间的1%,通过-XX:TLABWasteTargetPercent设置TLAB空间所占用Eden空间的百分比大小

4、一旦对象在TLAB空间分配内存失败,JVM就会尝试通过使用加锁机制确保数据操作的原子性,从而直接在Eden空间中分配内存

十、小结堆空间的参数设置

十一、堆是分配对象的唯一选择吗

随着JIT编译器的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术,将会导致一些微秒变化,所有对象分配到堆上渐渐变得不那么绝对了。

有一种特殊情况,如果经过逃逸分析后发现,一个对象并没有逃逸出方法的话,那么就可能被优化成栈上分配,这样无需堆上分配,也不需要垃圾回收了,也是最常见的堆外存储技术

逃逸分析

逃逸分析的基本行为就是分析对象动态作用域

  • 当一个对象在方法中定义后,对象只在方法内部使用,则认为没有发生逃逸
  • 当一个对象在方法中被定义后,它被外部方法引用,则认为发生逃逸,例如作为调用参数传递到其他地方中

1、栈上分配

如果未发生逃逸,将堆分配转为栈分配,如果一个对象在子程序中被分配,要使指向该对象的指针永远不会逃逸,对象可能是栈分配的候选,而不是堆分配

2、同步策略

如果一个对象被发现只能从一个线程被访问到,对于这个对象的操作可以不考虑同步

JIT编译器可以借助逃逸分析来判断同步块所使用的的锁对象,是否只能够被一个线程访问,而没有被发布到其他线程。如果没有,那么JIT编译器在编译这个同步块的时候,就会取消对这部分代码的同步。这样就大大提高并发性和性能,这个取消同步的过程就叫同步省略,也叫锁消除

3、分离对象或标量替换

有的对象可能不需要作为一个连续的内存结构存在,也可以被访问到,那么对象的部分(或全部)可以不存储在内存。而是存储在CPU寄存器中

标量是指一个无法再分解的更小的数据的数据。Java中原始数据类型就是标量

可以分解的数据叫聚合量,Java中的对象就是聚合量,因为他可以分解成其他聚合量和标量

标量替换参数:-XX:EliminateAllocations,默认打开

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

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

相关文章

80个10倍提升Excel技能的ChatGPT提示

你是否厌倦了在使用Excel时感觉像个新手?你是否想将你的技能提升到更高的水平,成为真正的Excel大师?嗯,如果你正在使用ChatGPT,那么成为Excel专家简直易如反掌。 你只需要了解一些最有用的Excel提示,就能在…

Kotlin 进阶函数式编程技巧

Kotlin 进阶函数式编程技巧 Kotlin 简介 软件开发环境不断变化,要求开发人员不仅适应,更要进化。Kotlin 以其简洁的语法和强大的功能迅速成为许多人进化过程中的信赖伙伴。虽然 Kotlin 的初始吸引力可能是它的简洁语法和与 Java 的互操作性&#xff0c…

教你烧录Jetson Orin Nano的ubuntu20.04镜像

Jetson Orin Nano烧录镜像 视频讲解 教你烧录Jetson Orin Nano的ubuntu20.04镜像 1. 下载sdk manager https://developer.nvidia.com/sdk-manager sudo dpkg -i xxxx.deb2. 进入recovery 插上typeC后,短接J14的FORCE_RECOVERY和GND,上电 如下图&#…

基于GB28181-2022实现web无插件播放H265视频

目前发布的GB28181-2022增加了对前端设备视频H265编码格式的支持,所以实现国标平台通过浏览器对H265视频流的无插件的解码播放将是未来的趋势。 目前大多的方案都是通过平台端把H265转码为H264,再推送到web前端进行解码播放,这种方式因为需要…

专访HuggingFace CTO:开源崛起、创业故事和AI民主化丨智源独家

导读 HuggingFace CTO Julien Chaumond认为,在大模型时代,AI民主化至关重要。随着大语言模型和复杂人工智能系统的崛起,持续提升AI技术的可及性有助于确保这些技术的获取和控制不集中在少数强大实体手中。技术民主化促进了机会均等&#xff0…

Java锁常见面试题

图片引用自:不可不说的Java“锁”事 - 美团技术团队 1 java内存模型 java内存模型(JMM)是线程间通信的控制机制。JMM定义了主内存和线程之间抽象关系。线程之间的共享变量存储在主内存中,每个线程都有一个私有的本地内存,本地内存中存储了该…

c语言从入门到实战——函数递归

函数递归 前言1. 递归是什么?2. 递归的限制条件3. 递归举例3.1 举例1:求n的阶乘3.1.1 分析和代码实现3.1.2 画图推演 3.2 举例2:3.2.1 分析和代码实现3.2.2 画图推演 4. 递归与迭代 前言 函数递归是指一个函数直接或间接地调用自身&#xff…

使用javafx,结合讯飞ai,搞了个ai聊天系统

第一步:先在讯飞ai那边获取接入的api 点进去,然后出现这个页面: 没有的话,就点击免费试用,有了的话,就点击服务管理: 用v2.0的和用3的都行,不过我推荐用2.0版本 文档位置&#xff1…

【每日一题】数组中两个数的最大异或值

文章目录 Tag题目来源题目解读解题思路方法一:哈希集合 其他语言python3 写在最后 Tag 【哈希集合】【位运算-异或和】【数组】【2023-11-04】 题目来源 421. 数组中两个数的最大异或值 题目解读 找出数组中两个数的最大异或结果。 解题思路 一看数据量达到了 …

Flink日志采集-ELK可视化实现

一、各组件版本 组件版本Flink1.16.1kafka2.0.0Logstash6.5.4Elasticseach6.3.1Kibana6.3.1 针对按照⽇志⽂件⼤⼩滚动⽣成⽂件的⽅式,可能因为某个错误的问题,需要看好多个⽇志⽂件,还有Flink on Yarn模式提交Flink任务,在任务执…

vSLAM中IMU预积分的作用--以惯性导航的角度分析

作为一个学过一点惯导的工程师,在初次接触视觉slam方向时,最感兴趣的就是IMU预积分了。但为什么要用这个预积分,在看了很多材料和书后,还是感觉模模糊糊,云里雾里。 在接触了vSLAM的更多内容后,站在历史研究…

如何避免 JavaScript 中的内存泄漏?

一、什么是内存泄漏? JavaScript 就是所谓的垃圾回收语言之一,垃圾回收语言通过定期检查哪些先前分配的内存仍然可以从应用程序的其他部分“访问”来帮助开发人员管理内存。垃圾回收语言中泄漏的主要原因是不需要的引用。如果你的 JavaScript 应用程序经…

java中:cmd界面输入javac后提示:找不到或无法加载主类,怎么解决

找不到或无法加载主类 检查环境变量cmd下用 java命令运行文件,提示找不到主类待续、更新中 检查环境变量 CLASSPATH 少写.;安装jdk过程有两部,一步为安装jdk文件夹,全部一致; 另一步为安装jre文件夹与jdk文件夹不一致(或者文件夹安装位置, 一路全部默认)path中将java变量移到顶…

js调整table表格上下相邻元素顺序

有时候我们会遇到要通过箭头控制table表格上下顺序的需求,如下: 点击向下就将该元素下移一位,下面的一位元素就移上来,点击向上就将该元素上移一位,上面的一位元素就移下来,也就是相邻元素互换位置顺序: <el-table :data="targetTable" border style=&quo…

HTTP 协议请求头 If-Match、If-None-Match 和 ETag

概述 在 HTTP 协议中&#xff0c;请求头 If-Match、If-None-Match、If-Modified-Since、If-Unmodified-Since、If-Range 主要是为了解决浏览器缓存数据而定义的请求头标准&#xff0c;按照协议规范正确的判断和使用这几个请求头&#xff0c;可以更精准的处理浏览器缓存&#x…

Springboot3整合Mybatis-plus3.5.3报错

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; 报错以及Bug ✨特色专栏&#xff1a; …

研发效能DevOps: Git安装

目录 一、理论 1.Git 2.Git 工具 二、实验 1.Git安装 2.配置Git 3. VS Code加载Git 一、理论 1.Git &#xff08;1&#xff09;简介 Git 是一个分布式版本控制及源代码管理工具;Git 可以为你的项目保存若干快照&#xff0c;以此来对整个项目进行版本管理。 Git 是一个…

ASTM F963-23美国玩具安全新标准发布

新标准发布 2023年10月13日&#xff0c;美国材料与试验协会&#xff08;ASTM&#xff09;发布了新版玩具安全标准ASTM F963-23。 主要更新内容 与ASTM F963-17相比&#xff0c;此次更新包括&#xff1a;单独描述了基材重金属元素的豁免情况&#xff0c;更新了邻苯二甲酸酯的管控…

Java与Redis的集成以及Redis中的项目应用

一、Java连接Redis Redis与MySQL都是数据库&#xff0c;java操作redis其实跟操作mysql的过程是一样的。 1.1 导入依赖 打开IDEA&#xff0c;进入Java项目&#xff0c;导入pom依赖&#xff0c;代码如下&#xff1a; <dependency><groupId>redis.clients</gro…

MySQL笔记--Ubuntu安装MySQL并基于C++测试API

目录 1--安装MySQL 2--MySQL连接 3--代码案例 1--安装MySQL # 安装MySQL-Server sudo apt install mysql-server# 设置系统启动时自动开启 sudo systemctl start mysql # sudo systemctl enable mysql# 检查MySQL运行状态 sudo systemctl status mysql# 进入MySQL终端 sudo…