【面经】讲一下你对jvm和jmm的了解

JVM

JVM是Java虚拟机,是Java程序的执行环境。它是一种虚拟的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现.

JVM是Java程序运行的核心,可以将Java字节码转换为可执行的机器码,提供了跨平台性、优秀的垃圾回收器,以及可靠的即时编译器

JVM内存结构

JVM通过程序计数器、虚拟机栈、本地方法栈、堆、方法区来管理内存和执行线程。

程序计数器–用于记录当前执行的字节码指令的行号,
虚拟机栈–用于存储每个方法的调用信息,
本地方法栈–用于存储本地方法的调用信息,
堆—用于存储对象实例和数组
方法区—用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据

变量存储的位置取决于其类型和存储方式。
在Java中,基本类型(如int、char、boolean等)的值是存储在栈(Stack)中的,
而引用类型(如String、Object等)的值则是存储在堆(Heap)中的

JVM中,线程共享的内存区域—堆(Heap)和方法区(Metaspace);
线程私有的内存区域----程序计数器(Program Counter Register)、Java虚拟机栈(Java Virtual Machine Stack)和本地方法栈(Native Method Stack)


JDK 8之前的永久代不是方法区
永久代(PermGen)是Java内存区域的一个特殊部分,主要用于存储类的元数据信息、常量池、静态变量等。
而方法区(Method Area)是另一个Java内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
虽然永久代和元空间都存储类的元数据信息,但它们在JDK 8之后被合并,并被称为元空间(Metaspace)。
在JDK 1.8下,JVM的内存结构发生了变化,引入了元空间(Metaspace)的概念,以替代之前的永久代(PermGen)。

元空间位于堆内存之外,用于存储类的元数据,它的最大空间大小默认情况下与老年代一样大,但可以通过JVM参数进行设置。元空间的出现解决了永久代中存在的一些问题,如GC无法清理永久代中等问题。

在JDK 1.8下,JVM的内存结构如下:

  1. 程序计数器:线程私有,用于记录当前执行的字节码指令的行号。
  2. Java虚拟机栈:线程私有,在方法执行时会创建一个栈帧,该栈帧包括了局部变量表、操作数栈、动态链接、方法出口等信息。
  3. 本地方法栈:线程私有,用于存储本地方法的调用信息。
  4. 堆:线程共享,用于存储对象实例和数组。在JDK 1.8中,堆内存被细分为新生代和老年代。
  5. 元空间:线程共享,用于存储类的元数据。元空间位于堆内存之外,它的最大空间大小可以通过JVM参数进行设置。

从Java 8开始,堆内存被细分为新生代和老年代,其中新生代又分为Eden区和两个Survivor区(Survivor0和Survivor1),这些区域都是所有线程共享的.

JVM内存参数配置

JVM的内存区域大小可以通过启动参数进行调整。例如,可以使用-Xms和-Xmx选项来设置堆的初始化和最大值。此外,还可以使用-XX:PermSize和-XX:MaxPermSize选项来调整永久代(在JDK8及以前使用)或元空间(Metaspace,JDK8以后使用)的初始化和最大值。

具体来说,可以按照以下步骤进行调整:

  1. 根据应用程序的需求确定堆内存的大小。例如,可以将-Xms和-Xmx选项设置为相同的大小,以充分利用系统内存。
  2. 根据物理内存的大小调整-Xms和-Xmx的值。例如,如果物理内存为4G,可以将-Xms和-Xmx的值设置为2G。
  3. 根据应用程序的特点选择适合的内存区域大小调整策略。例如,如果应用程序在大负载情况下会急剧地占用更多的内存,那么可能需要将-Xms和-Xmx的值设置得更大一些。
  4. 如果应用程序使用了永久代或元空间,也需要调整相应的内存区域大小。例如,可以使用-XX:PermSize和-XX:MaxPermSize选项来调整永久代的初始化和最大值,使用-XX:MetaspaceSize和-XX:MaxMetaspaceSize选项来调整元空间的初始化和最大值。

需要注意的是,在调整内存区域大小时,需要综合考虑系统的可用内存、物理内存的限制以及应用程序的需求等因素。如果设置的值过大,可能会导致系统崩溃或者出现其他问题;如果设置的值过小,则会影响应用程序的性能和稳定性。因此,需要进行适当的调整和测试,以找到最合适的值。

GC算法

垃圾回收(GC)是JVM自动管理内存的重要机制之一。它自动回收不再使用的对象占用的内存,以便重新分配给其他对象使用。
垃圾回收算法主要有两种:标记-清除(Mark-Sweep)和复制(Copying)。

标记-清除算法标记出所有活跃的对象,然后清除未被标记的对象。

复制算法将可用的内存空间划分为两个区域,一部分被标记为正在使用,另一部分被标记为未使用。当对象不再被使用时,它们将被移动到未使用的区域,并释放正在使用的内存空间。

什么是Full GC

Full GC是Java虚拟机中的一种垃圾回收方式。它会在整个堆空间中执行垃圾回收,清理所有不再被引用的对象所占用的内存空间。

通常,Full GC会在堆空间或者整个堆空间被占满时触发,它会清理所有的年老代对象和永久代对象。这是一种比较耗时的操作,因为它需要扫描整个堆空间,找到所有不再被引用的对象,并将它们标记为可回收的。

Full GC通常可以通过以下几种方式触发:

  1. 堆空间不足:当堆空间的可用空间不足以分配新对象时,会触发Full GC。在这种情况下,Full GC会清理整个堆空间。
  2. 调用System.gc()方法:调用System.gc()方法可以触发Full GC,虽然不能保证立即触发Full GC,但它可以提醒JVM尽快执行垃圾回收。
  3. 年老代空间满:当老年代空间被占满时,也会触发Full GC。如果老年代空间被占满,说明这些长时间存活的对象已经占用了大量的内存空间,需要进行Full GC来释放这些不再被引用的对象所占用的内存空间。

Full GC的作用是在JVM执行垃圾回收时对整个堆空间进行清理,释放不再被引用的对象所占用的内存空间,以便后续的对象分配。

垃圾回收器

垃圾回收器是JVM实现垃圾回收的具体工具。
不同的垃圾回收器在性能、吞吐量和内存使用等方面有所不同。
一些常见的垃圾回收器包括:Serial收集器、Parallel收集器、CMS(Concurrent Mark Sweep)收集器和G1(Garbage-First)收集器等。

Serial收集器是最简单的垃圾收集器,它在进行垃圾回收时会暂停所有其他工作线程。

Parallel收集器使用多个线程并行进行垃圾回收,以提高性能。

CMS收集器主要关注并发性,它在不影响应用程序性能的情况下进行垃圾回收。

G1收集器是一种面向停顿时间预测的收集器,它尽可能地降低垃圾收集停顿时间,并提高吞吐量


CMS是Java虚拟机中一种旧的垃圾回收器,用于执行老年代的垃圾回收。尽管CMS垃圾回收器在减小停顿时间方面表现出色,但CMS执行清理操作时可能会产生内存碎片,从而限制了老年代的可用空间,可能导致内存不足错误。CMS的并发执行会增加一定的CPU开销,因为它必须与应用程序并发运行。并且随着老年代中存活对象的增多,CMS的停顿时间可能会增加,甚至可能导致Full GC的发生

JVM调优

JVM调优主要是根据应用程序的具体需求和性能表现,调整JVM的参数和配置,以优化其性能和资源利用率。一些常见的JVM调优包括:调整堆大小、选择合适的垃圾回收器、调整线程数等。

在JDK 8及之前的版本中,可以通过以下JVM参数来调整堆的大小:

-Xms:设置JVM初始堆大小。例如,-Xms10m将初始堆大小设置为10MB。
-Xmx:设置JVM最大堆大小。例如,-Xmx20m将最大堆大小设置为20MB

选择并配置合适的垃圾回收器需要考虑多个因素,包括应用的性质、对停顿时间的要求、可用的硬件资源等。以下是一些基本建议:

  1. 如果应用对回收暂停时间要求不是特别高,且注重吞吐量的情况下可以选择PS收集器。
  2. 如果内存小于100M,使用串行收集器。
  3. 如果是单核、单机程序,并没有停顿时间的要求,使用串行收集器。
  4. 如果是多CPU、需要高吞吐量、允许停顿时间超过1秒,选择并行或者JVM自己选择。
  5. 如果是多CPU、追求低停顿时间,需快速响应(比如延迟不能超过1秒,如互联网应用),使用并发收集器。现在互联网的项目,基本都是使用G1。

要使用G1垃圾回收器,可以设置以下JVM参数:

  1. -XX:+UseG1GC:启用G1垃圾回收器。
  2. -XX:MaxGCPauseMillis=<value>:设置每次垃圾回收的最长停顿时间,单位是毫秒。
  3. -XX:G1HeapRegionSize=<value>:设置G1堆内存中每个Region的大小,必须是2次幂,最大是32MB。
  4. -XX:G1NewSizePercent=<value>:设置新生代占整个堆内存的比例。
  5. -XX:G1MaxNewSizePercent=<value>:设置新生代最大占整个堆内存的比例。
  6. -XX:G1MixedGCCountTarget=<value>:设置触发混合回收的GC次数目标。
  7. -XX:G1MixedGCLiveThresholdPercent=<value>:设置混合回收的存活阈值比例。
  8. -XX:G1RSetUpdatingPauseTimePercent=<value>:设置RSet更新时的停顿时间占比。
  9. -XX:G1HeapWastePercent=<value>:设置堆内存浪费比例。
  10. -XX:G1MixedGCCardTableThreshold=<value>:设置卡表阈值。

这些参数可以用来调整G1垃圾回收器的行为,以满足特定的性能需求。具体参数的含义和取值范围可能会因JVM版本和操作系统环境而有所不同,因此建议在调整参数之前阅读相关文档或咨询专业人士以获取准确的信息和指导。


JMM

JMM是Java内存模型,是Java虚拟机规范中定义的一种抽象的概念。
它规定了Java程序中线程和主内存之间的抽象关系,以及在多线程环境下如何访问共享变量的规则和行为。
JMM能够确保多线程环境下的数据安全和完整性,以及提高程序的性能和可靠性
JMM是Java虚拟机实现的一部分,它为Java程序提供了一种控制和一致性的内存访问方式。

JMM的三大特性包括

  1. 可见性:一个线程将自己工作内存内共享变量的最新值刷新回主内存,其他线程能收到主内存中共享变量值发生改变的通知,并能看到最新的改变。

  2. 原子性:一个线程在进行某个操作过程中,不可被打断,不可被分割,它要么执行成功,要么执行失败,不可处于一种中间状态。要求原子性是为了保证数据的完整性。

  3. 有序性:在JMM中,每个线程都按照各自独立的顺序执行,即每个线程的操作都遵循顺序执行的原则。同时,JMM通过同步机制来实现多个线程之间的有序性。

JMM的主要抽象概念是什么?

JMM的主要抽象概念包括:线程共享变量、原子性、可见性和有序性。
线程共享变量是多个线程可以访问的变量;
原子性是指操作不可分割,要么全部成功要么全部失败;
可见性是指一个线程对共享变量的修改对其他线程是立即可见的;
有序性是指程序在执行时必须遵循一些偏序关系。

JMM中的volatile关键字的作用是什么?

volatile关键字可以保证变量的可见性和有序性。
当一个变量被声明为volatile时,它可以保证多个线程之间对该变量的访问是一致的,
即每个线程都可以看到最新的值。
此外,volatile还可以保证一些指令的有序性,防止编译器进行重排。

JMM中的synchronized关键字的作用是什么?

synchronized关键字可以保证原子性和可见性。
当一个方法被声明为synchronized时,它可以保证该方法内的操作是原子的,即不会被其他线程干扰。
此外,synchronized还可以保证多个线程之间对共享变量的访问是可见的,即一个线程对共享变量的修改对其他线程是立即可见的。

JMM中的happens-before原则是什么?

happens-before原则是JMM中的一种关系规则,它定义了两个操作之间的偏序关系。
如果一个操作的结果需要对另一个操作可见,那么就称第一个操作发生在第二个操作之前(happens-before)。
JMM中规定了如果一个操作的结果需要对另一个操作可见,那么就存在一种happens-before关系。

JMM中的memory barrier是什么?

memory barrier是一种同步机制,它可以保证指令执行的顺序性和可见性。
JMM中规定了不同类型的memory barrier,包括LoadLoad、LoadStore、StoreLoad和StoreStore四种类型。
这些memory barrier可以用来保证指令执行的顺序性和可见性,从而实现正确的同步操作。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ThinkPet

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值