美团JVM面试题

news2024/11/18 5:32:31

1. 请解释一下对象创建的过程?

Java对象创建的过程主要分为以下五个步骤:

  • 类加载检查
    Java虚拟机在读取一条new指令时候,首先检查能否在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否被加载、解析和初始化。如果没有,则会先执行相应的类加载过程。

  • 内存分配
    在通过类加载检查后,则开始为新生的对象分配内存。该对象所需的内存大小在类加载完成后便可确定,因此为每个对象分配的内存大小是确定的。

     而分配方式主要有两种,分别为:
     (1)指针碰撞
     应用场合:堆内存规整(通俗的说就是用过的内存被整齐充分的利用,用过的内存放在一边,没有用过的放在另外一边,而中间利用一个分界值指针对这两边的内存进行分界,从而掌握内存分配情况)。
     即在开辟内存空间时候,将分界值指针往没用过的内存方向移动向应大小位置即可)。
     将堆内存这样划分的代表的GC收集器算法有:Serial,ParNew
     (2)空闲列表
     应用场合;堆内存不规整(虚拟机维护一个可以记录内存块是否可以用的列表来了解内存分配情况)
     即在开辟内存空间时候,找到一块足够大的内存块分配给该对象即可,同时更新记录列表。
     将堆内存这样划分的代表的GC收集器算法有:CMS
    
  • 初始化默认值
    在分配内存完成后,紧接着,虚拟机需要将分配到的内存空间都进行初始化(即给一些默认值),这样做是为了保证对象实例的字段在Java代码中可以在不赋初值的情况下使用。程序可以访问到这些字段对应数据类型的默认值。

  • 设置对象头
    初始化默认值完成后,虚拟机对对象进行一些简单设置,如标记该对象是哪个类的实例,这个对象的Hash码,该对象所处的年龄段等等(这些可以理解为对象实例的基本信息)。这些信息被写在对象头中。JVM根据当前的运行状态,会给出不同的设置方式。

  • 执行初始化方法
    在设置对象头完成后,最后执行由开发人员编写的对象的初始化方法,把对象按照开发人员的设计进行初始化,一个对象便创建出来了。

2. DCL(Double Check Lock)单例需不需要加volatile?

答: 需要添加volatile,否则可能会获取到半初始化对象从而引发程序未知错误!
单例对象用上volatile关键字,他的作用就是: 禁止指令重排序!


public class Singleton01 {

    public int total = 10;

    private static volatile Singleton01 instance;

    private Singleton01(){}

    public static Singleton01 getInstance(){
        if(instance == null){
            //Double Check Lock
            synchronized (Singleton01.class){
                if(instance == null){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    instance =  new Singleton01();
                }
            }

        }
        return  instance;
    }


    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            Singleton01 instance1 = Singleton01.getInstance();
            System.out.println(instance1.hashCode());
        }
    }
}


扩展知识:

  1. volatile的作用
    volatile只能用来修饰成员变量,它有两大特性:可见性、有序性,此处的有序性区别于synchornized的有序性。synchornized的有序性指的是多个线程要有序地执行临界区的代码,而volatile的有序性指的是指令有序性(指令不可重排)。
  2. 什么是指令重排序?
    指令重排序是指在运行程序时,处理器为了优化性能,代码的执行顺序可能不会按代码的顺序执行,比如前后两个赋值指令,第一条需要去磁盘读取数据,第二条需要去内存读取数据,程序在执行的时候可能会在去磁盘读取数据的时候先把第二条赋值指令执行完,等从磁盘读完数据再执行第一条赋值指令,这就叫指令重拍序,而volatile保证的有序性即防止指令重排。
    3.指令重排发生的条件?
    不影响单线程的最终一致性,也就是说,在单个线程内,两条指令变换顺序,不会影响最终结果,那么处理器可能会因为性能考虑指令重排,比如两条单纯赋值的指令

3.对象在内存中的存储布局?(对象和数组的存储不同)

对象在堆内存中的存储布局可以划分为三个部分:

  • 对象头(Header)
    HotSpot虚拟机对象的对象头部分包括三类信息:
    第一类(Mark Word)是用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等;
    另外一部分(Class Pointer)是类型指针,即对象指向它的类型元数据的指针。
    数组长度: 只有数组对象才有,在32位或者64位JVM中,长度都是32bit。

  • 实例数据(Instance Data)
    对象真正存储的有效信息,即我们在程序代码里面所定义的各种类型的字段内容,无论是从父类继承下来的,还是在子类中定义的字段都必须记录起来。

  • 对齐填充(Padding)
    对齐填充仅仅起着占位符的作用,将对象补齐到8字节的整数倍。
    由于虚拟机要求对象起始地址必须是8字节的整数倍,所以后面有几个字节用于把对象的大小补齐至8字节的整数倍,没有特别的功能,对齐填充不是必须存在的,仅仅是为了字节对齐。

为什么必须是8个字节?

根据“计算机组成原理”,8个字节是计算机读取和存储的最佳实践。

在这里插入图片描述

4. 对象头具体包括什么?

Java对象的对象头由三部分组成:

  • Mark Word
    MarkWord用于存储对象自身的运行时数据, 如哈希码(HashCode)、GC分代年龄、同步锁信息、偏向锁标识等等。Mark Word在32位JVM中的长度是32bit,在64位JVM中长度是64bit。

通常我们都是使用的64位的JVM,Mark Word 在64位 JVM 中内部结构如下图:
在这里插入图片描述

  • 类型指针
    类型指针指向对象的类元数据,虚拟机通过这个指针确定该对象是哪个类的实例。Java对象的类数据保存在方法区。

  • 数组长度(只有数组对象才有)
    如果对象是一个数组,那么对象头还需要有额外的空间用于存储数组的长度。如果对象是数组类型,因为JVM虚拟机可以通过Java对象的元数据信息确定Java对象的大小,但是无法从数组的元数据来确认数组的大小,所以用一块来记录数组长度。

5. 对象怎么定位?

比如:T t = new T(); 如何通过引用变量t 去 找到T的实例。
JVM的执行过程,其实是有两种方式的, (1)直接应用 和(2)句柄方式.

  • 直接引用
    优点: 直接访问
    缺点: GC需要移动对象的时候稍微麻烦
    在这里插入图片描述

  • 句柄方式
    优点: 对象小,垃圾回收时不用频繁改动t
    缺点: 两次访问
    在这里插入图片描述

6.对象怎么分配?

在这里插入图片描述
扩展知识:

  1. TLAB
    TLAB (Thread Local Allocation Buffer,线程本地分配缓冲区)是 Java 中内存分配的一个概念,它是在 Java 堆中划分出来的针对每个线程的内存区域,专门在该区域为该线程创建的对象分配内存。它的主要目的是在多线程并发环境下需要进行内存分配的时候,减少线程之间对于内存分配区域的竞争,加速内存分配的速度。TLAB 本质上还是在 Java 堆中的,因此在 TLAB 区域的对象,也可以被其他线程访问。

如果没有启用 TLAB,多个并发执行的线程需要创建对象、申请分配内存的时候,有可能在 Java 堆的同一个位置申请,这时就需要对拟分配的内存区域进行加锁或者采用 CAS 等操作,保证这个区域只能分配给一个线程。

启用了 TLAB 之后(-XX:+UseTLAB, 默认是开启的),JVM 会针对每一个线程在 Java 堆中预留一个内存区域,在预留这个动作发生的时候,需要进行加锁或者采用 CAS 等操作进行保护,避免多个线程预留同一个区域。一旦某个区域确定划分给某个线程,之后该线程需要分配内存的时候,会优先在这片区域中申请。这个区域针对分配内存这个动作而言是该线程私有的,因此在分配的时候不用进行加锁等保护性的操作。

  1. 逃逸分析
    逃逸分析,是一种可以有效减少Java 程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。通过逃逸分析,Java Hotspot编译器能够分析出一个新的对象的引用的使用范围从而决定是否要将这个对象分配到堆上
    在计算机语言编译器优化原理中,逃逸分析是指分析指针动态范围的方法,它同编译器优化原理的指针分析和外形分析相关联。当变量(或者对象)在方法中分配后,其指针有可能被返回或者被全局引用,这样就会被其他过程或者线程所引用,这种现象称作指针(或者引用)的逃逸(Escape)。

经过逃逸分析之后,可以得到三种对象的逃逸状态。

  • GlobalEscape(全局逃逸), 即一个对象的引用逃出了方法或者线程。例如,一个对象的引用是复制给了一个类变量,或者存储在在一个已经逃逸的对象当中,或者这个对象的引用作为方法的返回值返回给了调用方法。
  • ArgEscape(参数级逃逸),即在方法调用过程当中传递对象的应用给一个方法。这种状态可以通过分析被调方法的二进制代码确定。
  • NoEscape(没有逃逸),一个可以进行标量替换的对象。可以不将这种对象分配在传统的堆上。

编译器可以使用逃逸分析的结果,对程序进行一下优化。
a. 堆分配对象变成栈分配对象。一个方法当中的对象,对象的引用没有发生逃逸,那么这个方法可能会被分配在栈内存上而非常见的堆内存上。
b.消除同步。线程同步的代价是相当高的,同步的后果是降低并发性和性能。逃逸分析可以判断出某个对象是否始终只被一个线程访问,如果只被一个线程访问,那么对该对象的同步操作就可以转化成没有同步保护的操作,这样就能大大提高并发程度和性能。
c.矢量替代。逃逸分析方法如果发现对象的内存存储结构不需要连续进行的话,就可以将对象的部分甚至全部都保存在CPU寄存器内,这样能大大提高访问速度。

7.Object o = new Object(); 在内存中占用几个字节?

如果jvm没开启CompressedClassPointers类型指针压缩,那么首先new Object()占用8(markword)+8(class pointer)+0(instance data)+0(补齐为8的倍数)16个字节

8.为什么hotsport不使用C++对象来代表java对象?

因为C++对象有一个virtual table 这个是java对象所不需要也没有的。会占用内存

9.Class对象是在堆中还是在方法区中?

Class对象是存放在堆中
在这里插入图片描述

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

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

相关文章

数据库多表查询作业

数据库多表查询作业 创建数据库 插入数据 mysql> insert into student values(901,张老大,男,1985,计算机系,北京市海淀区),-> (902,张老二,男,1986,中文系,北京市昌平市),-> (903,张三,女,1990,中文系,湖南省永州市), -…

IT学不好没什么,大不了躺平

目录 一、个人经历 二、情绪调节技巧和策略 三&#xff1a;治愈自己 最近确实发生很多因为情绪失控发生意外的事情&#xff0c;有伤害别人的&#xff0c;也有伤害自己的&#xff0c;最近才听说过一个词叫呼吸性碱中毒&#xff0c;这就是情绪波动导致的身体不适&#xff0c;有…

layui实现左侧导航树形菜单

今日金句 战术上的勤奋并不能掩盖战略上的懒惰 文章目录 前言一、左侧导航1.1 概述1.2 树形菜单 二、导入数据表及无限级分类2.1 导入数据2.2 无限级分类 三、Book实例3.1 环境准备3.3 导入封装的工具类3.3 实体类及dao方法的编写3.4 编写Servlet3.5 编写Jsp 前言 在上篇博客…

【miniQMT实盘量化2】与客户端建立连接

前言 上篇从概念上介绍了miniQMT和它的优势&#xff0c;本篇开始实操的第一步&#xff1a;连接客户端&#xff0c;让你知其然&#xff0c;也只其所以然&#xff0c;话不多说&#xff0c;开干&#xff01; 登录客户端 一切的开始&#xff0c;我们要首先保证QMT极简版客户端的…

deeplabv3+源码之慢慢解析 第二章datasets文件夹(2)voc.py--VOCSegmentation类

系列文章目录 第一章deeplabv3源码之慢慢解析 根目录(1)main.py–get_argparser函数 第一章deeplabv3源码之慢慢解析 根目录(2)main.py–get_dataset函数 第一章deeplabv3源码之慢慢解析 根目录(3)main.py–validate函数 第一章deeplabv3源码之慢慢解析 根目录(4)main.py–mai…

MySQl数据库第八课-------SQL命令查询-------主要命脉

作者前言 欢迎小可爱们前来借鉴我的gtiee秦老大大 (qin-laoda) - Gitee.com —————————————————————————————— 目录 查询数据 条件 逻辑运算符 模糊查询 范围查询 in 判断空 UNION 排序 聚合 分组&#xff1a;group by —————————…

安卓进度条:ProgressBar和Seekbar

一、ProgressBar进度条介绍 ProgressBar 是 Android 中的一个进度条控件&#xff0c;用于显示正在进行的任务的进度。它可以以水平或圆形的形式展示进度&#xff0c;并提供了多种样式和属性来满足不同的需求。 相关属性&#xff1a; android:progress&#xff1a;设置进度条的…

LangChain大型语言模型(LLM)应用开发(六):Agents

LangChain是一个基于大语言模型&#xff08;如ChatGPT&#xff09;用于构建端到端语言模型应用的 Python 框架。它提供了一套工具、组件和接口&#xff0c;可简化创建由大型语言模型 (LLM) 和聊天模型提供支持的应用程序的过程。LangChain 可以轻松管理与语言模型的交互&#x…

【Linux指令集】---zip指令(超详细)

个人主页&#xff1a;平行线也会相交 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【Linux专栏】&#x1f388; 本专栏旨在分享学习Linux的一点学习心得&#xff0c;欢迎大家在评论区讨论&#x1f48c; 演示环境&#xff1…

DevOps系列文章 之 pipeline 流水线:企业微信群通知消息

部署流程 开发环境Jenkins的job进行编译-打包-build成镜像-推送到镜像私有仓库-部署开发环境-&#xff08;开发自测&#xff09;-自测通过-提测。 版本管理&#xff1a; 构建的过程&#xff1a; 开发环境通过验证&#xff0c;则点击“Yes”&#xff0c;没有则Abort。点击Yes后…

在Linux系统中,如何搭建DNS服务

如何搭建DNS服务 要在Linux系统上搭建DNS服务&#xff0c;你可以按照以下步骤进行操作&#xff1a; 1.安装BIND软件包&#xff1a; sudo yum install bind bind-utils2.配置主DNS服务器&#xff1a; 打开/etc/named.conf文件&#xff0c;编辑DNS服务器的配置。根据你的域名和…

122、仿真-基于51单片机的电量监测电压电流和温度报警系统设计(Proteus仿真+程序+流程图+配套资料等)

方案选择 单片机的选择 方案一&#xff1a;STM32系列单片机控制&#xff0c;该型号单片机为LQFP44封装&#xff0c;内部资源足够用于本次设计。STM32F103系列芯片最高工作频率可达72MHZ&#xff0c;在存储器的01等等待周期仿真时可达到1.25Mip/MHZ(Dhrystone2.1)。内部128k字节…

自然语言处理(扩展学习1):Scheduled Sampling(计划采样)与2. Teacher forcing(教师强制)

自然语言处理&#xff08;扩展学习1&#xff09;&#xff1a;Scheduled Sampling(计划采样)与2. Teacher forcing&#xff08;教师强制&#xff09; 作者&#xff1a;安静到无声 个人主页 作者简介&#xff1a;人工智能和硬件设计博士生、CSDN与阿里云开发者博客专家&#xff0…

C/C++动态内存开辟(详解)

目录 一&#xff0c;mallloc 函数参数&#xff1a; 函数原理&#xff1a; 二&#xff0c;calloc 函数参数&#xff1a; 函数原理&#xff1a; 三&#xff0c;realloc 函数参数&#xff1a; 函数原理: 五&#xff0c;小结 2&#xff09;对开辟空间的越界访问 3&#x…

cnn分类图像cifar10

使用CNN模型来分类图像&#xff0c;数据集采用的cifar10&#xff0c;cifar10共有6万张&#xff0c;这些图像共分为10类。 命名的格式大概是这样的&#xff1a;0_19761.jpg&#xff0c;它的第一个数字表示的就是图像所属的类&#xff0c;分成清楚的就知道了&#xff0c;第0类就是…

Flutter:EasyLoading(loading加载、消息提示)

前言 官方虽然提供了内置的加载指示器和提示信息&#xff0c;但是功能比较简陋&#xff0c;这里推荐&#xff1a;flutter_easyloading CircularProgressIndicator CircularProgressIndicator()加粗样式 ScaffoldMessenger.of(context).showSnackBar(const SnackBar(// 提示…

MySQL(三)SQL优化、Buffer pool、Change buffer

MySQL系列文章 MySQL&#xff08;一&#xff09;基本架构、SQL语句操作、试图 MySQL&#xff08;二&#xff09;索引原理以及优化 MySQL&#xff08;三&#xff09;SQL优化、Buffer pool、Change buffer MySQL&#xff08;四&#xff09;事务原理及分析 MySQL&#xff08;五&a…

泛积木-低代码 搭建 增删改查

文章首发于 增删改查 。 这里我们以增删改查作为示例&#xff0c;演示下从页面创建到各个功能齐全。创建页面的时候&#xff0c;建议接口先写好&#xff0c;当然也可以一边联调一边写接口&#xff0c;当前对增删改查提供以下测试接口&#xff1a; 测试接口 /contactsList 列…

【数据结构】非线性结构之树结构(含堆)

前言 前面的三篇文章已经将线性结构讲述完毕了&#xff0c;下面的文章将会为大家将讲点新东西&#xff1a;非线性结构中的树结构。萌新对这里的知识点相对陌生&#xff0c;建议反复观看&#xff01;&#xff01; 关于线性结构的三篇文章放在下面&#xff1a; 线性表之顺序表 线…

数组与指针

博客内容&#xff1a;数组与指针 文章目录 一、 数组&#xff1f;指针&#xff1f;1.区别与联系大小赋值存储位置 二、指针数组、数组指针&#xff1f;二维数组和二级指针&数组名与数组的区别总结 一、 数组&#xff1f;指针&#xff1f; 数组 相同类型数据的集合 指针 指…