【JVM】运行时数据区之 堆——自问自答

news2024/11/30 8:35:19

Q:堆和栈,在设计上有何用义?

此处我们不说数据结构的概念。

堆本身是一种存储结构,在代码的内存层面来看,无论是c++ 操作的原生内存,还是Java 背后的JVM,堆的作用都是进行持久存储的。

这个持久存储并不是像数据库那样,而是指在程序运行的生命周期里,在一个进程映像中。堆负责存储一些程序运行时生命周期较长的变量、对象等。

这些变量、对象 把他们的地址空间暴露给我们,方便操作,同时,也需要在不用的时候进行堆内存的释放。比如C++的new 和delete, java的对象创建与垃圾回收。

堆:是一种原材料仓库,就像饭店的冰箱。

而栈更像是一个临时空间,当我们需要执行具体的操作,执行一些方法或函数时,栈可以临时存储一些变量,指令。当我们执行结束的时候,这些临时变量就会随着栈顶元素的弹出而销毁。(这就是在C/C++中为什么不允许返回局部变量的地址或引用)。

所以,栈是不需要进行回收操作的。

栈:是一种临时处理区域,就像厨师的条案。

Q: JVM的堆的概述

一个JVM实例只存在一个堆内存,堆也是Java内存管理的核心区域Java 堆区在JVM 启动的时候即被创建,其空间大小也就确定了。是JVM管理的最大一块内存空间。

堆整体上分为 年轻代+老年代

> 堆内存的大小是可以调节的。

-Xms         堆的初始值大小 等价于:-XX:InitialHeapSize

-Xmx      堆的最大值大小  等价于:-XX:MaxHeapSize

比如 

设置堆的初始值为6兆

-Xms6291456
-Xms6144k
-Xms6m

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


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


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

查看设置的参数:方式一: jps 查看当前进程号,   jstat -gc 进程id


方式=: -XX:+PrintGCDetails


《Java虚拟机规范》规定,堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的。
所有的线程共享Java堆,在这里还可以划分线程私有的缓冲区 (ThreadLocal Allocation Buffer, TLAB)。

《Java虚拟机规范》中对Java堆的描述是:所有的对象实例以及数组都应当在运行时分配在堆上。(The heap is the run-time data area fromwhich memory for all class instances and arrays is allocated我要说的是:“几乎”所有的对象实例都在这里分配内存。一从实际使用角度看的。


数组和对象可能永远不会存储在栈上,因为栈中保存引用,这个引用指向对象或者数组在堆中的位置。


在方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才会被移除。


堆,是GC ( Garbage Collection,垃圾收集器) 执行垃圾回收的重点区域。

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

简而言之:

元空间是jdk8之后的落地实现。

逻辑上来说,元空间也算是堆,但实际管理上,方法区有自己的管理方式

Q: 年轻代与老年代

存储在JVM中的Java对象可以被划分为两类:

 一类是生命周期较短的瞬时对象,这类对象的创建和消亡都非常迅速

  另外一类对象的生命周期却非常长,在某些极端的情况下还能够与JVM的生命周期保持一致。


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

默认比例:

配置新生代与老年代在堆结构的占比。
        默认-XX:NewRatio=2,表示新生代占1,老年代占2,新生代占整个堆的1/3

        可以修改-XX:NewRatio=4,表示新生代占1,老年代占4,新生代占整个堆的1/5
       

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

几乎所有的Java对象都是在Eden区被new出来的。绝大部分的Java对象的销毁都在新生代进行了。

IBM公司的专门研究表明,新生代中 80的对象都是“朝生死”的可以使用选项”-xmn”设置新生代最大内存大小
这个参数一般使用默认值就可以了。

查看具体设置的参数值

jps

jinfo -flag  参数名 进程号

通过jstat -gc PID 来看看每个区域的内存使用情况

xxC 表示总量 ,xxU 表示已使用

我们注意到以下问题,幸存区和Eden区比例并不是 1:1: 8,而是1:6,其实这个还是要手动去设置的,(你jinfo查出来的参数骗了你)在官网也可以看到具体描述

另一个参数:

UseAdaptiveSizePolicy,即使用自适应策略。 (这个参数跟垃圾回收设置有关,在后面讲)

当我们不想使用自适应,要求按照我们自己设置的比例时,请带上这个参数:

-XX:-UseAdaptiveSizePolicy

参数表达中 :-,表示关闭某一参数,:+ 表示开启某一参数

Q:对象分配过程

1、new的对象先放伊甸园区。此区有大小限制。
2、当伊甸园的空间填满时,程序又需要创建对象,JVM的垃圾回收器将对年轻代进行垃圾回收(Minor GC/youngGC),将伊园区中的不再被其他对象所引用的对象进行销毁。再加载新的对象放到伊园区
3、然后将伊甸园中的剩余对象移动到幸存者0区。

4、如果再次触发垃圾回收,此时上次幸存下来的放在幸存者0区的,如果没有回收,就会放到幸存者1区。
5、如果再次经历垃圾回收,此时会重新放回幸存者0区,接着再去幸存者1区

6、啥时候能去养老区呢?可以设置次数。默认是15次。
可以设置参数: -XX:MaxTenuringThreshold=<N>进行设置

7.在养老区,相对悠闲。当养老区内存不足时,再次触发GC: Major GC,进行全堆的内存清理。
8.若养老区执行了Major GC之后发现依然无法进行对象的保存,就会产生OOM异常

        java.lang.OutofMemoryError: Java heap space

总结:
针对幸存者s0,s1区的总结: 复制之后有交换,谁空谁是to.
关于垃圾回收: 频繁在新生区收集,很少在养老区收集,几乎不在永久区元空间收集。

注意:

  1. Eden区满,触发YoungGC/MinorGC,回收整个年轻代。
  2. 老年代满,触发Major GC
  3. 触发Full GC的情况:

  1. 方法区空间不足。
  2. 调用System.gc()(可能触发),
  3. 老年代空间不足,通过Minor GC后进入老年代的平均大小大于老年代的可用内存,
  4. 由Eden区、survivor space (From Space) 区向survivor space1 (Topace)区复制时,对象大小大于To Space可用内存,则把该对象晋升到老年代,且老年代的可用内存小于该对象大小。

GC是会触发STW的 FUllGC时间最长,开发中尽量避免使用FULL GC

Q:内存分配策略

针对不同年龄段的对象分配原则如下所示:

  •   优先分配到Eden
  •   大对象直接分配到老年代(尽量避免程序中出现过多的大对象)
  •   长期存活的对象分配到老年代
  •    动态对象年龄判断

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

  • 空间分配担保   

        -XX:HandlePromotionFailure

关于空间分配担保:

Q:TLAB

1、为什么有TLAB ( Thread Local Allocation Buffer ) ?


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


2、什么是TLAB?


从内存模型而不是垃圾收集的角度,对Eden区域继续进行划分,JVM为每个线程分配了一个私有缓存区域,它包含在Eden空间内。
多线程同时分配内存时,使用TLAB可以避免一系列的非线程安全问题同时还能够提升内存分配的吞吐量,因此我们可以将这种内存分配方式称之为快速分配策略
据我所知所有openJDK衍生出来的JVM都提供了TLAB的设计。

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

  • 在程序中,开发人员可以通过选项“-XX:UseTLAB”设置是否开启TLAB空间。

  • 默认情况下,TLAB空间的内存非常小,仅占有整个Eden空间的1%,当然我们可以通过选项“-XX:TLABWasteTargetPercent”设置TLAB空间所占用Eden空间的百分比大小。

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

Q:堆空间相关参数设置:

官网:

java (oracle.com)

-xx:+PrintFlagsInitial 查看所有的参数的默认初始值

-xx:+PrintFlagsFinal 查看所有的参数的最终值 (可能会存在修改不再是初始值)
-Xms 初始堆空间内存(默认为物理内存的1/64)
-xmx 最大堆空间内存(默认为物理内存的1/4)
-Xmn 设置新生代的大小。(初始值及最大值):
-xx:NewRatio配置新生代与老年代在堆结构的占比


-XX:survivorRatio  设置新生代中Eden和so/s1空间的比例
-XX:MaxTenuringThreshold 设置新生代垃圾的最大年龄
-XX:+PrintGCDetails 输出详细的GC处理日志
打印gc简要信息:  1、-XX:+PrintGC    2、 -verbose:gc

-XX:HandlepromotionFailure: 是否设置空间分配担保

 

思考两个问题:

  • 如果参数-XX: SurviorRatio 参数设置的特别大会怎样?

    此参数表示新生代中,Eden区和Survior区的比例,一般默认设为8,表示

    Eden:s1:s0 =8:1 :1

    若设置的过大:

这种情况下,当触发YGC时,说明Eden区已经无法容纳新对象,此时,新生代的垃圾被回收,Eden区的一部分对象进入to区,同样幸存区的from的对象也进入to。

但是由于Eden很大,S区很小,可能出现很多对象无法进入S区,只能直接被晋升进养老代。

结论:Eden设置的比例过大,会让YGC/Minor GC失去意义,GC分代的思想也不能很好的体现。(对象没到阈值就直接晋升)

  • 如果Eden区设置的过小呢?

这种情况下,由于Eden区很小,很快就会满,一旦满了,就要触发YGC /Minor GC, GC线程与用户线程一般来说是串行执行,会造成STW,这样程序执行效率就很低

Q:堆优化之 逃逸分析,标量替换,同步省略

使用逃逸分析,编译器可以对代码做如下优化:


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


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


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

JIT编译器在编译期间根据逃逸分析的结果,发现如果一个对象并没有逃逸出方法的话,就可能被优化成栈上分配。分配完成后,继续在调用栈内执行,最后线程结束,栈空间被回收,局部变量对象也被回收。这样就无须进行垃圾回收了。
常见的栈上分配的场景
在逃逸分析中,已经说明了。分别是给成员变量赋值、方法返回值实例引用传递。

1、逃逸分析

逃逸分析示例代码

-XX:+DoEscapeAnalysis 开启逃逸分析

2、同步省略(锁消除)

线程同步的代价是相当高的,同步的后果是降低并发性和性能。


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

3、标量替换

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

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

相关文章

集合-Collection

系列文章目录 1.集合-Collection-CSDN博客 文章目录 目录 系列文章目录 文章目录 前言 一 . 集合的继承体系 二 . 什么是Collection? 三 . 常用方法 1.add(Object element): 将指定的元素添加到集合中。 2. remove(Object element): 从集合中移除指定的元素。 3. bo…

国庆day1---消息队列实现进程之间通信方式代码,现象

snd&#xff1a; #include <myhead.h>#define ERR_MSG(msg) do{\fprintf(stderr,"__%d__:",__LINE__);\perror(msg);\ }while(0)typedef struct{ long msgtype; //消息类型char data[1024]; //消息正文 }Msg;#define SIZE sizeof(Msg)-sizeof(long)int main…

HP E1740A 模拟量输入模块

HP&#xff08;惠普&#xff09;E1740A 模拟量输入模块是一种用于数据采集和测量的工控模块&#xff0c;通常用于各种自动化和监测应用中。以下是该模拟量输入模块的一些可能特点和功能&#xff1a; 多通道输入&#xff1a; E1740A 模块通常具有多个模拟量输入通道&#xff0c;…

windows的arp响应

1.原理‘ 2.场景 3.步骤

YOLOv8+swin_transfomerv2

测试环境&#xff1a;cuda11.3 pytorch1.11 rtx3090 wsl2 ubuntu20.04 踩了很多坑&#xff0c;网上很多博主的代码根本跑不通&#xff0c;自己去github仓库复现修改的 网上博主的代码日常出现cpu,gpu混合&#xff0c;或许是人家分布式训练了&#xff0c;哈哈哈 下面上干货…

Android回收视图

本文所有代码均存放于https://github.com/MADMAX110/BitsandPizzas 回收视图是列表视图的一个更高级也更灵活的版本。 回收视图比列表视图更加灵活&#xff0c;所以需要更多设置&#xff0c;回收视图使用一个适配器访问它的数据&#xff0c;不过与列表视图不同&#xff0c;回收…

[RCTF2015]EasySQL 二次注入 regexp指定字段 reverse逆序输出

第一眼没看出来 我以为是伪造管理员 就先去测试管理员账号 去register.php 注册 首先先注册一个自己的账号 我喜欢用admin123 发现里面存在修改密码的内容 那么肯定链接到数据库了 题目又提示是sql 那我们看看能不能修改管理员密码 首先我们猜测闭合 通过用户名 admin…

HTML,CSS,JavaScript知识点

HTML&#xff0c;CSS&#xff0c;JavaScript知识点 HTML篇 HTML是超文本标记语言。文件以.html结尾。 Hello,HTML。常用的工具: 标题: <h1>一级标题</h1><h2>二级标题</h2><h3>三级标题</h3><h4>四级标题</h4>无序列表和有…

YOLOv8+swin_transfomer

测试环境&#xff1a;cuda11.3 pytorch1.11 rtx3090 wsl2 ubuntu20.04 本科在读&#xff0c;中九以上老师或者课题组捞捞我&#xff0c;孩子想读书&#xff0c;求课题组师兄内推qaq 踩了很多坑&#xff0c;网上很多博主的代码根本跑不通&#xff0c;自己去github仓库复现修…

PHP免登录积分商城系统/动力商城/积分商城兑换系统源码Tinkphp

介绍&#xff1a; PHP免登录积分商城系统/动力商城/积分商城兑换系统源码Tinkphp&#xff0c;这个免登录积分商城系统是一种新型的电子商务模式&#xff0c;它通过省去麻烦的注册步骤&#xff0c;让用户能够很快又方便去积分兑换。这种商城系统具有UI干净整洁大方、运行顺畅的…

正点原子嵌入式linux驱动开发——STM32MP1启动详解

STM32单片机是直接将程序下载到内部 Flash中&#xff0c;上电以后直接运行内部 Flash中的程序。 STM32MP157内部没有供用户使用的 Flash&#xff0c;系统都是存放在外部 Flash里面的&#xff0c;比如 EMMC、NAND等&#xff0c;因此 STM32MP157上电以后需要从外部 Flash加载程序…

Mendix中的依赖管理:npm和Maven的应用

序言 在传统java开发项目中&#xff0c;我们可以利用maven来管理jar包依赖&#xff0c;但在mendix项目开发Custom Java Action时&#xff0c;由于目录结构有一些差异&#xff0c;我们需要自行配置。同样的&#xff0c;在mendix项目开发Custom JavaScript Action时&#xff0c;…

调度算法2-适用于交互式系统

一、时间片轮转调度算法(RR) 1.算法思想 Round-Robin 公平、轮流地为各个进程服务&#xff0c;让每个进程在一定时间间隔内都可得到响应 2.算法规则 按照各进程到达就绪队列的顺序&#xff0c;轮流让各个进程执行一个时间片 响应比(等待时间要求服务时间)/要求服务时间 3…

http请求报错:406 Not Acceptable的解决办法

目录 应用场景 基本概念 解决方法 方法一&#xff1a; 方法二&#xff1a; 方法三&#xff1a; 应用场景 接口在返回结果集的时候出现了406的报错&#xff0c;但是返回String类型不会报错&#xff0c;正常返回。 基本概念 406 Not Acceptable是一个HTTP响应状态码&…

Vue+ElementUI实现动态树和表格数据的分页模糊查询

目录 前言 一、动态树的实现 1.数据表 2.编写后端controller层 3.定义前端发送请求路径 4.前端左侧动态树的编写 4.1.发送请求获取数据 4.2.遍历左侧菜单 5.实现左侧菜单点击展示右边内容 5.1.定义组件 5.2.定义组件与路由的对应关系 5.3.渲染组件内容 5.4.通过动态…

Spring整合第三方框架

目录 Spring整合第三方框架 加载外部properties文件 自定义命名空间解析原理 自定义命名空间总结和案例需求 总结 案例 Spring整合第三方框架 加载外部properties文件 Spring整合第三方框架不像MyBatis那么简单了&#xff0c;例如Dubbo框架在与Spring框架整合时&#xf…

苹果CMS插件-苹果CMS全套插件免费

网站内容的生成和管理对于网站所有者和内容创作者来说是一个挑战。有一些强大的工具可以帮助您轻松地解决这些问题。苹果CMS插件自动采集插件、采集发布插件以及采集伪原创发布插件&#xff0c;是这些工具之一。它们不仅可以极大地节省您的时间和精力&#xff0c;还可以提高您网…

Python编码规范与代码优化

博主&#xff1a;命运之光 专栏&#xff1a;Python程序设计 Python编码规范 Python的程序由包、模块&#xff08;即一个Python文件&#xff09;、函数、类和语句组成 (1) 命名规则 变量名、包名、模块名通常采用小写字母开头&#xff0c;如果名称中包含多个单词&#xff0c;一…

【Axure】Axure的常用功能

选择 分为相交选中和包含选中 相交选中&#xff1a;部分选中即是选中包含选中&#xff1a;全选才是选中 缩放 按住元件四角&#xff0c;等比例缩放 置顶和置底 所谓置于顶层就是不被后来的元件覆盖住&#xff0c;置于底层的意思则相反 组合、对齐、分布 组合&#xff1…

redis主从从,redis-7.0.13

redis主从从&#xff0c;redis-7.0.13 下载redis安装redis安装redis-7.0.13过程报错1、没有gcc&#xff0c;报错2、没有python3&#xff0c;报错3、[adlist.o] 错误 127 解决安装报错安装完成 部署redis 主从从结构redis主服务器配置redis启动redis登录redisredis默认是主 redi…