我的面试八股(JVM篇)

这篇具有很好参考价值的文章主要介绍了我的面试八股(JVM篇)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

谈一谈Java内存区域和Java内存模型的理解? / Java内存区域和Java内存模型是一个东西吗?

Java内存区域和Java内存模型不是一个东西!!!!!

Java内存区域,也就是Java运行时数据区域。是指Java虚拟机在运行时创建的一个内存区域,用于存储Java程序运行时所需要的数据结构和对象实例。Java运行时数据区包括堆、方法区、虚拟机栈、本地方法栈和程序计数器等部分。

Java内存模型,也就是JMM,定义了程序中各个变量的访问规则,在虚拟机中将变量存储到内存和从内存中取出变量这种底层细节。
我的面试八股(JVM篇)

JMM

JMM(Java Memory Model)是Java虚拟机规范中定义的一种内存模型,它描述了Java程序如何在多线程环境下访问共享内存。JMM主要是为了屏蔽各种硬件和操作系统对内存访问的差异而定义出来的内存模型。JMM定义了一个抽象的计算机内存模型,包括主内存工作内存两部分。
我的面试八股(JVM篇)

  • 主内存
    主内存是所有线程共享的内存区域,也是Java内存模型中的核心部分。主内存中保存着Java对象的实例数据、类信息、方法等。
    在多线程环境下,当一个线程修改了主内存中的共享变量时,其他线程并不会立即看到这个变量的修改。这是因为每个线程都有自己的工作内存,与主内存之间存在缓存数据不一致的问题。
  • 工作内存
    每个线程都有自己的工作内存(Thread Local Memory),它是线程私有的内存区域。工作内存中保存着该线程使用到的共享变量的副本拷贝。
    当一个线程需要使用某个共享变量时,它会首先从主内存中读取该变量的值到自己的工作内存中,并对它进行操作。操作完成后,该线程再将变量的值写回主内存中。在这个过程中,其他线程并不能直接访问到该线程的工作内存。
  • 内存交互操作
    Java内存模型还定义了一些内存交互操作,包括lock、unlock、read和write等。这些操作可以保证多线程环境下共享变量的可见性和一致性。
    • lock和unlock:用于对共享变量进行加锁和解锁,确保同一时刻只有一个线程可以访问该变量。
    • read:用于将工作内存中的值传递到主内存中。
    • write:用于将主内存中的值传递到工作内存中。
  • 内存屏障
    Java内存模型还定义了内存屏障(Memory Barrier),用于控制内存交互操作的顺序和可见性。
    内存屏障分为四种类型:
    • LoadLoad屏障:保证load指令之前的所有load指令已经执行完毕。
    • StoreStore屏障:保证store指令之前的所有store指令已经执行完毕。
    • LoadStore屏障:保证load指令之前的所有指令都已经执行完毕,并且能够读取到最新的变量值。
    • StoreLoad屏障:保证store指令之前的所有指令都已经执行完毕,并且该指令所写入的变量值对于其他线程可见。

JMM的作用

保证多线程环境下的数据可见性、原子性和有序性。通过JMM规定的规范,我们可以确保多线程的正确性和可靠性。

synchronized、volatile、Lock等关键字和API有什么作用?

这些关键字和API是Java多线程编程中用来实现同步的机制,用于保证多线程环境下共享变量的可见性、有序性和原子性。其中synchronized关键字用于实现悲观锁机制,volatile关键字用于实现轻量级同步机制,Lock接口用于实现更加灵活的锁机制。

Java内存区域

1.8之前

我的面试八股(JVM篇)

1.8及之后

我的面试八股(JVM篇)
我的面试八股(JVM篇)

字符串常量池?

字符串常量池 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。

HotSpot 虚拟机中字符串常量池的实现是 src/hotspot/share/classfile/stringTable.cpp ,StringTable 本质上就是一个HashSet ,容量为 StringTableSize(可以通过 -XX:StringTableSize 参数来设置)。

StringTable 中保存的是字符串对象的引用,字符串对象的引用指向堆中的字符串对象。

JDK1.7 之前,字符串常量池存放在永久代。JDK1.7 字符串常量池和静态变量从永久代移动了 Java 堆中。

JDK 1.7 为什么要将字符串常量池移动到堆中?

主要是因为永久代(方法区实现)的 GC 回收效率太低,只有在整堆收集 (Full GC)的时候才会被执行 GC。Java 程序中通常会有大量的被创建的字符串等待回收,将字符串常量池放到堆中,能够更高效及时地回收字符串内存。

什么是直接内存?

直接内存是一种特殊的内存缓冲区,并不在 Java 堆或方法区中分配的,而是通过 JNI 的方式在本地内存上分配的。

直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用。而且也可能导致 OutOfMemoryError 错误出现。

JDK1.4 中新加入的 NIO(Non-Blocking I/O,也被称为New I/O),引入了一种基于通道(Channel)与缓存区(Buffer)的 I/O 方式,它可以直接使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样就能在一些场景中显著提高性能,因为避免了在 Java 堆和 Native 堆之间来回复制数据。

直接内存的分配不会受到 Java 堆的限制,但是,既然是内存就会受到本机总内存大小以及处理器寻址空间的限制。

说一说HotSpot 虚拟机在 Java 堆中对象分配过程?

  • 类加载检查
    虚拟机遇到一条 new 指令时,首先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有,那必须先执行相应的类加载过程。
  • 分配内存
    在类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需的内存大小在类加载完成后便可确定,为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来。分配方式有 “指针碰撞” 和 “空闲列表” 两种,选择哪种分配方式由 Java 堆是否规整决定,而 Java 堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。
  • 初始化零值
    内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。
  • 设置对象头
    初始化零值完成之后,虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的 GC 分代年龄等信息。 这些信息存放在对象头中。 另外,根据虚拟机当前运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。
  • 执行init方法
    在上面工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生了,但从 Java 程序的视角来看,对象创建才刚开始, 方法还没有执行,所有的字段都还为零。所以一般来说,执行 new 指令之后会接着执行 方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来。

虚拟机内存分配的两种方式?

  • 指针碰撞

    • 适用场合:堆内存规整(没有内存碎片)的情况下
    • 原理:用过的内存全部整合到一边,没有用过的内存放到另一边,中间一个分解指针,只需要想着没用过的内存方向将该指针移动对象内存大小即可。
    • 使用该分配方式的GC收集器:Serial,ParNew
  • 空闲列表

    • 适用场合:堆内存不规整的情况下
    • 原理:虚拟机会维护一个列表,该列表中会记录哪些内存块是可用的,在分配的时候,找一块儿足够大的内存块儿来划分给对象实例,最后更新列表记录
    • 使用该分配方式的 GC 收集器:CMS

选择以上两种方式中的哪一种,取决于 Java 堆内存是否规整。而 Java 堆内存是否规整,取决于 GC 收集器的算法是"标记-清除",还是"标记-整理"(也称作"标记-压缩"),值得注意的是,复制算法内存也是规整的

说一说对象创建中不安全的现象? / 如何解决线程不安全的问题?

对象创建在虚拟机中是非常频繁的行为, 即使仅仅修改一个指针所指向的位置, 在并发情况下也并不是线程安全的, 可能出现正在给对象A分配内存, 指针还没来得及修改, 对象B又同时使用了原来的指针来分配内存的情况。

一种是对分配内存空间的动作进行同步处理——实际上虚拟机是采用CAS配上失败重试的方式保证更新操作的原子性; 另外一种是把内存分配的动作按照线程划分在不同的空间之中进行, 即每个线程在Java堆中预先分配一小块内存, 称为本地线程分配缓冲(Thread Local Allocation Buffer, TLAB) , 哪个线程要分配内存, 就在哪个线程的本地缓冲区中分配, 只有本地缓冲区用完了, 分配新的缓存区时才需要同步锁定。 虚拟机是否使用TLAB, 可以通过-XX: +/-UseTLAB参数来设定。

对象的内存布局?

在HotSpot虚拟机里, 对象在堆内存中的存储布局可以划分为三个部分: 对象头(Header) 、 实例数据(Instance Data) 和对齐填充(Padding) 。

HotSpot虚拟机对象的对象头部分包括两类信息。 第一类是用于存储对象自身的运行时数据, 如哈希码(HashCode) 、 GC分代年龄、 锁状态标志、 线程持有的锁、 偏向线程ID、 偏向时间戳等, 这部分数据的长度在32位和64位的虚拟机(未开启压缩指针) 中分别为32个比特和64个比特, 官方称它为“Mark Word”。

对象头的另外一部分是类型指针, 即对象指向它的类型元数据的指针, Java虚拟机通过这个指针来确定该对象是哪个类的实例。

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

对象的第三部分是对齐填充, 这并不是必然存在的, 也没有特别的含义, 它仅仅起着占位符的作用。 由于HotSpot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍, 换句话说就是任何对象的大小都必须是8字节的整数倍。 对象头部分已经被精心设计成正好是8字节的倍数(1倍或者
2倍) , 因此, 如果对象实例数据部分没有对齐的话, 就需要通过对齐填充来补全。文章来源地址https://www.toymoban.com/news/detail-414858.html

到了这里,关于我的面试八股(JVM篇)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • java八股文面试[JVM]——双亲委派模型

    1.当 AppClassLoader 去加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委托给父加载器 ExtClassLoader 去完成。 2.当ExtClassLoader去加载一个class时,它首先也不会去尝试加载这个类,而是把类加载请求委托给父加载器 BootstrapClassLoader 去完成。 3.如果BootstrapClas

    2024年02月11日
    浏览(53)
  • 深入理解多线程编程和 JVM 内存模型

    目录 一、什么是多线程编程 二、JVM介绍 三、 JVM 内存模型 多线程编程是一种编程方式,它允许程序在同一时间内执行多个线程或任务。线程是程序执行的最小单位,多线程编程可以将任务拆分为多个线程,每个线程独立执行特定的操作或任务。 在传统的单线程编程中,程

    2024年01月23日
    浏览(46)
  • 面试系列 - JVM内存模型和调优详解

    目录 一、JVM内存模型 1. 程序计数器(Program Counter Register): 2.Java虚拟机栈(Java Virtual Machine Stacks): 3. 本地方法栈(Native Method Stack): 5. 方法区(Method Area): 6. 运行时常量池(Runtime Constant Pool): 7. 直接内存(Direct Memory): 二、垃圾回收期CMS和G1区别 1. 工作原理:

    2024年02月10日
    浏览(45)
  • JVM包含哪几部分?JVM内存模型?线程的生命周期? 对Spring AOP的理解?布隆过滤器

    JVM由三部分组成:类加载子系统、执行引擎、运行时数据区。 类加载子系统:可以根据指定的全限定名来载入类或接口。 执行引擎:负责执行那些包含在被载入类的方法中的指令。 运行时数据区:当程序运行时,JVM需要内存来存储许多内容,例如:字节码、对象、参数、返

    2024年02月16日
    浏览(45)
  • 【JVM】JVM五大内存区域介绍

    目录  一、程序计数器(线程私有) 二、java虚拟机栈(线程私有) 2.1、虚拟机栈 2.2、栈相关测试 2.2.1、栈溢出 三、本地方法栈(线程私有) 四、java堆(线程共享) 五、方法区(线程共享) 六、实例演示         Java虚拟机在执行Java程序的过程中会把它所管理的内存划

    2024年02月15日
    浏览(43)
  • java-JVM内存区域&JVM运行时内存

    JVM 内存区域主要分为线程私有区域【程序计数器、虚拟机栈、本地方法区】、线程共享区域【JAVA 堆、方法区】、直接内存。 线程私有数据区域生命周期与线程相同, 依赖用户线程的启动/结束 而 创建/销毁(在 HotspotVM 内, 每个线程都与操作系统的本地线程直接映射, 因此这部

    2024年02月12日
    浏览(47)
  • JVM | Java内存区域

    以上笔记基于JavaGuide整理 JDK 1.8: 线程私有 的:程序计数器,虚拟机栈,本地方法栈 线程共享 的:堆,方法区(元空间),直接内存 (非运行时数据区的一部分) 程序计数器有两个作用: 字节码解释器通过改变程序计数器来依次读取指令,从而 实现代码的流程控制 ,如:顺

    2024年02月08日
    浏览(79)
  • JVM内存区域划分

    简介:JVM有很多个不同版本的实现;其中HotSpot VM是最主流使用的JVM;Oracle官方jdk和开源openjdk都是使用这个JVM JVM内存区域划分:按每个区域不同功能划分;相互之间不会干扰 本地方法栈 native:表示是JVM内部C++代码;给调用JVM内部方法准备的栈空间。存储本地方法native之间调

    2024年02月12日
    浏览(37)
  • jvm的内存划分区域

    java虚拟机栈、本地方法栈、堆、程序计数器、方法区。     1.本地方法栈:用于管理本地方法的调用,里面并没有我们写的代码逻辑,其由native修饰,由 C 语言实现。 2.程序计数器:它是一块很小的内存空间,主要用来记录各个线程执行的字节码的地址,例如,分支、循环、

    2024年02月11日
    浏览(45)
  • JVM内存区域

    为了更好的理解类加载和垃圾回收,先要了解一下JVM的内存区域(如果没有特殊说明,都是针对的是 HotSpot 虚拟机。)。 Java 源代码文件经过编译器编译后生成字节码文件,然后交给 JVM 的类加载器,加载完毕后,交给执行引擎执行。在整个执行的过程中,JVM 会用一块空间来

    2024年02月13日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包