JVM面试整理--对象的创建和堆

文章目录

    • 对象的创建过程是怎样的?
    • 对象在内存中的结构是怎样的(专业的叫法:对象的内存布局)
    • 对象在内存分配时使用的哪种方式(有的地方也称为:分配算法)
    • 知道什么是“指针碰撞”吗?
    • 知道什么是“空闲列表”吗?
    • 内存分配算法是否存在并发问题?是如何解决并发问题的?
    • 请说一说“TLAB”
    • 对象创建好后,如何对其进行定位访问
    • jvm中堆的结构是怎样的?新生代、老年代
    • JVM中老年代和新生代的比例是多少?
    • 说说对象的分配规则。
    • 什么是空间分配担保?

 


对象的创建过程是怎样的?

答:
对象的创建过程大致可分为如下四个步骤:

  • 类加载检查。JVM遇到 new 指令,会去检查能否在常量池中定位到类的符号引用,并且检查该符号引用代表的类是否已经被加载、解析和初始化过。如果没有,则进行类加载过程。
  • 为对象分配内存。内存分配方式可以选择“指针碰撞”或是“空闲列表”。具体选择哪一种,是由堆内存是否规整决定的。
  • 将内存空间初始化为零值(除对象头)。这一步保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用。
  • 为对象进行必要的信息设置。这些信息主要存放在对象头(Object Header)之中。对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的 GC 分代年龄等信息。还有关于对象是否加锁的信息。
    对象头的内容如下:

对象头

助记方法:
1.类是对象的基础,只有加载到jvm的类,才可以创建其对象 -> 类加载检查。
2.对象是需要存储信息的 -> 就需要为其分配内存。
3.对象的信息称为实例变量,实例变量需要赋值才可使用 -> 初始化零值
4.对象头保存了对象的元数据->这些信息是在对象创建时设置的

 


对象在内存中的结构是怎样的(专业的叫法:对象的内存布局)

答:
再次引用前面的图:
对象头
 
对象在堆内存中存储,其内存布局可分为3部分:
对象头、实例数据和对齐填充。

  • 对象头(Object Header)。
    又可再分为两部分:第一部分,官方称为“MarkWord”,存储的是对象自身的运行时数据。如:哈希码(HashCode)、GC分代年龄、锁状态标志、偏向线程ID等;第二部分是“类型指针”(Klass Pointer)。即对象指向它的类元数据的指针,虚拟机可通过其确定该对象是哪个类的实例。如果是数组对象,其对象头还会有一块用于记录数组长度的数据。
     
  • 实例数据(Instance Data)。
    真正存储有效信息的部分,这里主要指对象的实例字段。包含从父类继承的字段信息,相同宽度的字段总是分配在一起。
     
  • 对齐填充(Padding)
    因为对象的大小必须是8字节的整数倍。对象头是8字节的整数倍,但是实例数据则不一定,当其大小不足8字节时,需要对其进行填充,以达到8字节。

下图给出了对象头的空间占用情况:

对象头的空间占用

 


对象在内存分配时使用的哪种方式(有的地方也称为:分配算法)

答:对象在内存分配时,会使用到两种内存分配方式:

  • 指针碰撞
  • 空闲列表

具体使用哪一种内存分配方式,取决于堆内存是否规整。而堆内存是否规整,则决定于垃圾收集器是否带有整理功能(即是否采用了“标记-整理”垃圾收集算法)。

因此,带压缩功能的Serial New等收集器,采用 “指针碰撞” 分配算法。
而像CMS这种基于 Mark-Sweep 算法的收集器,则采用 “空闲列表” 分配算法。

 


知道什么是“指针碰撞”吗?

答:

指针碰撞

一般情况下,JVM的对象都放在堆内存中(发生逃逸分析除外)。
Java虚拟机为新生对象分配内存时。如果Java堆中的内存是绝对规整的,所有被使用过的的内存都被放到一边,空闲的内存放在另外一边,中间以一个指针作为分界点的指示器(如图所示)。那么分配内存所要做的仅仅是把指针向空闲内存方向挪动,挪动的距离正好等于对象的大小,这种内存分配方式就称为“指针碰撞”。

助记点:
1.堆内存需绝对规整(结合图加深理解)。关键字:已使用内存空闲的内存指针分界点的指示器
2.分配内存,即指针向空闲内存方向移动。关键词:向空闲内存的方向移动
3.内存分配方式,称为“指针碰撞”。关键词:内存分配方式

 


知道什么是“空闲列表”吗?

答:
堆内存不规整 (即已使用内存和空闲内存是交错在一起的)时,是不适合使用“指针碰撞”的方式为对象分配内存的。

而对象的内存是需要一整块连续区域的。为了能快速找到合适大小的内存,虚拟机就需要维护一个列表,用于记录哪些内存可用,其大小是多少

当分配内存时,借助于该列表可以很快找到合适的内存。列表中已分配内存的记录需要进行更新(删除还是打标已使用?)。
这种内存分配方式,称为“空闲列表”。

 


内存分配算法是否存在并发问题?是如何解决并发问题的?

答:
内存分配时,不管是使用 “指针碰撞” 还是 “空闲列表” 分配算法,都有可能产生并发问题。

因为可能有多个线程发起了对象创建,从而出现多个线程对同一个内存位置的争抢情况,结果导致某些线程中对象创建失败。

为了解决并发问题,JVM给出了两种方案:

  • CAS + 失败重试
  • TLAB(本地线程分配缓冲)

 


请说一说“TLAB”

答:TLAB是本地线程分配缓冲(Thread Local Allocation Buffer)的缩写。有的地方也翻译为本地线程分配缓存。

它的工作原理:为每一个线程在堆中预先分配一小块内存,称其为TLAB。为线程分配堆内存时,优先从TLAB上分配,当TLAB用完后,再使用同步锁定的方式分配新的TLAB。

虚拟机通过参数 -XX:+/-UseTLAB 决定是否启用TLAB功能。

 


对象创建好后,如何对其进行定位访问

答:对象创建好后,可以通过如下方式对其进行定位访问:

  • 使用句柄
  • 直接指针

使用句柄访问, 将会从堆中划分出一块内存来作为句柄池,reference(栈中的本地变量表) 中存储的是对象的句柄地址,而句柄中包含了对象实例数据指针与对象类型数据指针(二元组)。

使用句柄访问:
在这里插入图片描述

使用句柄访问的好处:

reference 中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而reference本身不需要修改。

助记关键词:
句柄池句柄地址-存在本地变量表中句柄是一个二元组<实例数据指针, 类型数据指针>

 
使用直接指针访问,reference 中存储的直接就是对象地址。而对象的头信息中包含对象类型数据的信息(称为对象类型指针) 。

使用直接指针访问:
使用直接指针访问

直接指针访问的好处:

速度更快,节省了一次指针定位的时间开销。

助记关键词:
reference直接指向对象地址对象头中包含类型信息

 


jvm中堆的结构是怎样的?新生代、老年代

答:
Java堆可分为新生代和老年代,新生代和老年代的比例为1:2。这个比例可通过-XX:NewRatio参数进行调整。

新生代又可细分为一块较大的Eden区和两块较小的Survivor区。默认Eden和Survivor大小的比例是8:1。这个比例可以通过 -XX:SurvivorRatio 参数进行调整。

JVM每次只会使用Eden和其中一块Survivor区域来为对象服务,所以总是有一块Survivor区域是空闲的。
所以新生代的实际可用内存空间为9/10的新生代内存空间。

新生代存储的都是新创建的对象、比较小的对象;而老年代存的都是活得久的,或是比较大的对象。所以,老年代占JVM堆内存的比例较大。

下图是新生代和老年代的比例:

新生代和老年代

 


JVM中老年代和新生代的比例是多少?

答:新生代和老年代的默认比例是1:2。这个比例可通过-XX:NewRatio参数进行调整。

注:和堆的内存结构重复,单独拎出来是为了强调该知识点的重要性。

 


说说对象的分配规则。

答:
目前主流的虚拟机都是使用的分代收集算法。对应的内存的分配也是分代分配的。

规则如下:

  • 对象优先分配在Eden区。如果启用了TLAB,则优先在TLAB上分配。当Eden区没有足够空间时,会触发一次Minor GC(YGC)。
  • 大对象(指需要大量连续内存空间的对象,如长字符串和长数组)直接进入老年代。
  • 长期存活的对象进入老年代。对象头的MarkWord中有一个GC分代年龄,默认分代年龄大于15的对象进入老年代。对象首次进入Survivor区后,其对象年龄设为1,然后在Survivor中每熬过一次Minor GC,其对象年龄就加1,直至对象年龄达到阈值,对象直接进入老年代(也称为晋升为老年代)。可通过设置参数-XX:MaxTenuringThreshold修改对象进入老年代的年龄阈值。该规则针对的是Survivor中的对象
  • 动态对象年龄判断。其概念为:如果Survivor中相同年龄的对象的总大小大于Survivor空间的一半,年龄大于等于该年龄的对象,直接进入老年代。注意:这条规则也是针对Survivor中的对象的

    例如:Survivor空间=2M,当中有4个对象:
    对象1:大小-0.5M,年龄-3
    对象2:大小-0.6M,年龄-3
    对象3:大小-0.1M,年龄-2
    对象4:大小-0.2M,年龄-4
     
    根据动态对象年龄判断的规则:对象1+对象2的总大小 > 1M;
    所以,Survivor中年龄大于等于3的对象,都将进入老年代。
    故:对象1、对象2和对象4,都将进入老年代。

  • 空间分配担保。

助记关键词:
Eden区-优先分配大对象Survivor-长期存活的对象Survivor-基于同龄对象占比空间分配担保

参考:
《深入理解Java虚拟机(第2版)》p95、3.6节

 


什么是空间分配担保?

答:
每次在进行Minor GC之前,JVM会检查老年代最大可用连续空间是否大于新生代所有对象的总空间

如果大于,说明Minor GC是安全的,进行Minor GC。

帮助理解 =>(因为Minor GC是对新生代空间的回收,如果其中的对象还需要使用,则需要将其移动到老年代,所以就需要老年代有足够的空间存放这些对象。这里考虑的是最极端的情况,即所有的新生代对象都将进入老年代

如果老年代的最大可用连续空间,放不下所有的新生代对象,那么是否大于历次晋升到老年代的对象的平均大小。如果满足,则进行Minor GC,否则进行Full GC。

担保是由老年代作出的,并且主要发生在第二次判断中。老年代根据历史的经验值,对Minor GC的结果作出担保,保证GC后老年代的最大连续空间可以容纳GC后晋升到老年代的对象大小。

担保依然有失败的可能,因为Minor GC后存活的对象可能会很多,以至于大于老年代的可用连续空间。所以就有了第三个判断。

如果担保失败,则在Minor GC后,会再次触发一次Full GC(如图所示)。

下图演示了“空间分配担保”的流程:

空间分配担保

JDK6 Update24之前的空间分配担保流程如下:

空间分配担保-02

 
 

声明:大部分图片来源自互联网和书籍。

 
 
 


参考:

  • [1] 《深入理解Java虚拟机(第2版)》

  • [2] https://blog.csdn.net/qyj19920704/article/details/123965383

  • [3] https://blog.csdn.net/guorui_java/article/details/137178686

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

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

相关文章

LeetCode 80—— 删除有序数组中的重复项 II

阅读目录 1. 题目2. 解题思路3. 代码实现 1. 题目 2. 解题思路 让 index指向删除重复元素后数组的新长度&#xff1b;让 st_idx 指向重复元素的起始位置&#xff0c;而 i 指向重复元素的结束位置&#xff0c;duplicate_num代表重复元素的个数&#xff1b;一段重复元素结束后&am…

php校园活动报名系统vue+mysql

开发语言&#xff1a;php 后端框架&#xff1a;Thinkphp/Laravel 前端框架&#xff1a;vue.js 服务器&#xff1a;apache 数据库&#xff1a;mysql 运行环境:phpstudy/wamp/xammp等本选题则旨在通过标签分类管理等方式&#xff0c;管理员&#xff1b;首页、个人中心、学生管理、…

Redis 缓存穿透、缓存击穿、缓存雪崩区别和解决方案

缓存穿透 什么是缓存穿透&#xff1f; 缓存穿透说简单点就是大量请求的 key 是不合理的&#xff0c;根本不存在于缓存中&#xff0c;也不存在于数据库中 。这就导致这些请求直接到了数据库上&#xff0c;根本没有经过缓存这一层&#xff0c;对数据库造成了巨大的压力&#xf…

2、Qt UI控件 -- qucsdk项目使用

前言&#xff1a;上一篇文章讲了qucsdk的环境部署&#xff0c;可以在QDesigner和Qt Creator中看到qucsdk控件&#xff0c;这一篇来讲下在项目中使用qucsdk库中的控件。 一、准备材料 要想使用第三方库&#xff0c;需要三个先决条件&#xff0c; 1、控件的头文件 2、动/静态链…

记录vue之npm run serve报错SET NODE_OPTIONS

> vue-antd-pro3.0.0 serve > SET NODE_OPTIONS--openssl-legacy-provider && vue-cli-service servesh: SET: command not found 一定要注意&#xff1a;将 SET NODE_OPTIONS–openssl-legacy-provider && 删除即可

17 - Games101 - 笔记 - 材质与外观

**17 **材质与外观 材质与BRDF 自然界中的材质&#xff1a;丝绸、头发、蝴蝶翅膀表面、寿司表面等等 图形学中的材质&#xff1a;同一个模型之所以渲染出不同结果的原因就是因为材质。在图形学中是给不同的物体指定不同的材质&#xff0c;知道它们如何和光线作用后就能正确的…

C++11 数据结构0 什么是 “数据结构“?数据,数据对象,数据元素,数据项 概念。算法的基本概念 和 算法的度量,大O表示法,空间换时间的代码

数据&#xff1a; 是能输入计算机且能被计算机处理的各种符号的集合。数值型的数据&#xff1a;整数和实数。非数值型的数据&#xff1a;文字、图像、图形、声音等。 数据对象&#xff1a; 性质相同的 "数据元素" 的集合 例如一个 int arr[10], Teacher tea[3]; 数…

汽车4S行业的信息化特点与BI建设挑战

汽车行业也是一个非常大的行业&#xff0c;上下游非常广&#xff0c;像主机厂&#xff0c;上游的零配件&#xff0c;下游的汽车流通&#xff0c;汽车流通之后的汽车后市场&#xff0c;整个链条比较长。今天主要讲的是汽车流通&#xff0c;汽车4S集团。一个汽车4S集团下面授权代…

MySQL高级篇(存储引擎InnoDB、MyISAM、Memory)

目录 1、存储引擎简介 1.1、查询建表语句&#xff0c;默认存储引擎&#xff1a;InnoDB 1.2、查看当前数据库支持的存储引擎 1.3、创建表&#xff0c;并指定存储引擎 2、 存储引擎-InnoDB介绍 2.1、存储引擎特点 3、MyISAM存储引擎 4、Memory存储引擎 5、InnoDB、MyISAM、Memory…

HTML基础(3)

1、内联框架 iframe用于在网页内显示网页&#xff0c;语法如下&#xff1a; <iframe src"URL"></iframe> URL指向隔离页面 hight&#xff0c;weight设置高宽&#xff0c;删除边框将frameborder设置为0 <td> <iframe frameborder"0&qu…

麒麟v10安装mysql-8.0.35

因为要修复漏洞的原因&#xff0c;这两天将麒麟v10操作系统的服务器上的MySQL版本由5.7.27升级到8.0.35&#xff08;mysql安装包下载地址&#xff1a;MySQL :: Download MySQL Community Server (Archived Versions)&#xff09;&#xff0c;mysql的安装过程主要参考了这个博主…

JavaScript-throw、try,2024年前端高级面试题总结

提交电话 二、xml初识 xml文件是用来做什么的 核心思想&#xff1a; 答&#xff1a;存储数据 延伸问题&#xff1a; xml是怎样存储数据的&#xff1f; 答&#xff1a;以标签的形式存储 例: coco 什么是xml元素? 元素该如何编写? xml中的元素其实就是一个个的标签 标签…

面试官为什么喜欢考察Vue底层原理

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

国产低代码工具,轻松搞定数据迁移

在日常的业务系统升级或者数据维护过程中&#xff0c;数据迁移是各个企业用户不得不面临的问题&#xff0c;尤其是数据迁移过程中要保障数据完整性、统一性和及时性&#xff0c;同时也需要注意源数据中的数据质量问题&#xff0c;比如缺失、无效、错误等问题&#xff0c;需要在…

前端面试算法题1

1.已知&#xff1a; • 布局分为&#xff1a;父元素A和N个子元素B&#xff1b; • A宽度不固定&#xff1a;最小宽度为1000px&#xff0c;内部边距是32px • B的宽度不固定&#xff1a;相邻两个B元素的间距是16px&#xff0c;所有B的宽度相同&#xff0c;边框为1像素&#x…

【JAVA语言-第19话】多线程详细解析(一)

目录 多线程 1.1 并发和并行 1.2 线程和进程 1.2.1 进程 1.2.2 线程 1.3 单线程 1.3.1 单线程案例 1.4 创建多线程的方式 1.4.1 继承Thread类 1.4.2 实现Runnable接口 1.4.3 使用匿名内部类 1.5 Thread类 1.5.1 构造方法 1.5.2 常用方法 1.5.3 Thread类中…

《QT实用小工具·二十四》各种数学和数据的坐标演示图

1、概述 源码放在文章末尾 该项目实现了各种数学和数据的坐标演示图&#xff0c;下面是demo演示&#xff1a; 项目部分代码如下&#xff1a; #ifndef FRMMAIN_H #define FRMMAIN_H#include <QWidget> class QAbstractButton;namespace Ui { class frmMain; }class fr…

吴恩达机器学习-异常检测(Anomaly Detection)

在本练习中&#xff0c;您将实现异常检测算法&#xff0c;并将其应用于检测网络上出现故障的服务器。 文章目录 1-包2-异常检测2.1问题陈述2.2数据集2.3高斯分布2.2.1高斯实现的估计参数&#xff1a;2.2.2选择阈值&#x1d716; 2.4高维数据集 1-包 首先&#xff0c;让我们运…

基于FPGA的以太网相关文章导航

首先需要了解以太网的一些接口协议标准&#xff0c;常见的MII、GMII、RGMII时序&#xff0c;便于后续开发。 【必读】从MII到RGMII&#xff0c;一文了解以太网PHY芯片不同传输接口信号时序&#xff01; 介绍一款比较老的以太网PHY芯片88E1518&#xff0c;具有RGMII接口&#xf…

代码随想录阅读笔记-二叉树【总结】

二叉树的理论基础 代码随想录 (programmercarl.com)&#xff1a;二叉树的种类、存储方式、遍历方式、定义方式 二叉树的遍历方式 深度优先遍历 代码随想录阅读笔记-二叉树【递归遍历】-CSDN博客&#xff1a;递归三部曲初次亮相代码随想录阅读笔记-二叉树【迭代遍历】-CSDN博…