【Java面试】二十、JVM篇(上):JVM结构

2024-06-19 07:12
文章标签 java jvm 面试 结构 二十

本文主要是介绍【Java面试】二十、JVM篇(上):JVM结构,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 1、JVM
  • 2、程序计数器
  • 3、堆
  • 4、栈
    • 4.1 垃圾回收是否涉及栈内存
    • 4.2 栈内存分配越大越好吗
    • 4.3 方法内的局部变量是否线程安全吗
    • 4.4 栈内存溢出的情况
    • 4.5 堆和栈的区别是什么
  • 5、方法区
    • 5.1 常量池
    • 5.2 运行时常量池
  • 6、直接内存

1、JVM

Java源码编译成class字节码后,JVM负责class字节码的处理。不同操作系统下的JVM,将字节码处理成对应操作系统下的二进制文件,从而实现一次编译,到处运行。能到处运行,是因为不同操作系统有不同的JVM,也即编译后的class字节码和操作系统之间,隔着一个JVM在干活儿。

简单的理解,JVM就是Java二进制字节码的运行环境,是JDK包下包含的一些代码,像一个虚拟的计算机一样处理着class字节码。
在这里插入图片描述

JVM有两个最大的亮点:

  • 处理class字节码,实现一次编译,到处运行
  • 自动内存管理,垃圾回收机制,创建的对象,用完后不用手动回收

JVM的组成:

在这里插入图片描述

2、程序计数器

线程私有的,每个线程各有一份,内部保存的字节码的行号。用于记录正在执行的字节码指令的地址。

在这里插入图片描述

如上,线程1 执行字节码从第0行到第10行后,CPU时间片用完,线程1的程序计数器记录下当前行号。CPU的时间片分给了线程2 ,线程2执行到第9行后,CPU时间片用完。线程1再次抢到时间片,继续执行,此时,根据线程1的程序计数器,CPU就知道该从第10行继续往下执行。

3、堆

  • 堆区,线程共享
  • 保存着创建出来的对象
  • use、total、max,use == max 后,无法再分配空间,堆内存溢出

堆中有年轻代和老年代。年轻代有三部分,伊甸园区和两块大小相同的幸存者区。根据JVM策略,新创建的对象在伊甸园区,伊甸园区满了以后,触发Young GC,GC后或者的对象,复制到幸存者区,后面每GC一次,活着的对象年龄加一(对象头里存着年龄),对象GC年龄到达阈值(如15)后,晋升到老年代。

在这里插入图片描述

元空间里存类的信息、静态变量、常量、编译后的字节码信息(InstanceKlass对象(c++))。对JDK7和8,其位置变化:

  • JDK7及以前,方法区在堆区的永久代空间里
  • JDK8及以后,永久代被移除,用元空间代替,方法区在元空间,而元空间在操作系统的直接内存里,理论上可以一直分配

在这里插入图片描述

因为永久代/方法区或者说后来的元空间,存储的主要是一些类信息和常量,需求开发,加载的类越来越多,这个空间不可控,移除永久代,在本地内存放个元空间,可以防止OOM

4、栈

栈,即每个线程运行时需要的内存空间,保存着该线程方法调用的基本数据,先进后出,其中,每一个调用的方法用一个叫栈帧的东西存。

在这里插入图片描述

4.1 垃圾回收是否涉及栈内存

垃圾回收处理的主要是堆内存,对于栈内存,栈帧弹栈后,内存就会释放

4.2 栈内存分配越大越好吗

默认1024k,栈帧过大会导致线程数变少,机器总内存为512m,目前能活动的线程数则为512个,如果把内存改为2048k,则可活动的线程数上限在栈内存方面就会减半

4.3 方法内的局部变量是否线程安全吗

在方法的作用范围之内,是线程安全的。出了方法,被怎么使用就不一定了。如下:

在这里插入图片描述

每个线程过来,都会在自己的栈里创建一个m1方法对应的栈帧,栈帧里存着局部变量sb,因此线程安全。

对m2方法来说,局部变量sb是传过来的,那可能就有线程安全问题,如上面main线程中在操作sb,新开的一个线程也在操作sb。同理,m3方法,将局部变量sb return,后面可能被一个成员变量接收,但后面本质上也不关局部变量的事了

4.4 栈内存溢出的情况

  • 栈帧过多导致栈内存溢出,如递归调用
public static void m1() {m1();
}
  • 栈帧过大导致栈内存溢出

4.5 堆和栈的区别是什么

  • 栈内存一般会用来存储局部变量和方法调用,但堆内存是用来存储Java对象和数组的
  • 堆会GC垃圾回收,而栈不会
  • 栈内存是线程私有的,而堆内存是线程共有的
  • 两者异常错误不同,但如果栈内存或者堆内存不足都会抛出异常,栈空间不足:Java.ang.StackOverFlowError,堆空间不足:java.lang.OutOfMemoryError.

5、方法区

  • 方法区和堆一样,各个线程共享
  • 主要存储类的信息(InstanceKclass对象)、运行时常量池
  • 虚拟机启动的时候创建,虚拟机关闭的时候释放
  • 方法区的内存空间不够时,抛异常OutOfMemoryError:Metaspace
  • 方法区是一个概念,不同版本的JDK有不同的实现,对JDK7来说,永久代是其对方法区的落地实现(且此时永久代在堆区),对JDK8来说,则给方法区换了一种实现:元空间(元空间在本地内存)

在这里插入图片描述

5.1 常量池

javap查看一个类是字节码的结构信息:可以看到类的基本信息、常量池、方法的定义:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
常量池可以看作是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息

在这里插入图片描述

5.2 运行时常量池

上面提到,常量池是class文件中的,当这个类被加载,它的常量池信息就会放入到运行时常量池,并发里面的符号地址变为真实地址

在这里插入图片描述

常量池和运行时常量池的区别:

在这里插入图片描述

【区别】

6、直接内存

直接内存(操作系统分给JVM进程的内存之外的内存),不属于JVM内存,不由JVM进行管理。在进行NIO操作时,用于数据缓冲区,其分配回收成本高,但读写性能好。常规IO复制文件流程:

在这里插入图片描述

NIO复制文件流程:这块直接内存系统和Java代码都可以直接访问,少了一次缓冲区的复制操作

在这里插入图片描述

直接内存主要通过 java.nio 包下的 ByteBuffer 类来进行分配和使用。

ByteBuffer.allocateDirect(int capacity)

直接内存可以减少数据在 Java 堆和操作系统内存之间的拷贝次数,从而提高 I/O 的效率,常用于文件读写

【相关】

这篇关于【Java面试】二十、JVM篇(上):JVM结构的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1074351

相关文章

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

Spring AI集成DeepSeek的详细步骤

《SpringAI集成DeepSeek的详细步骤》DeepSeek作为一款卓越的国产AI模型,越来越多的公司考虑在自己的应用中集成,对于Java应用来说,我们可以借助SpringAI集成DeepSe... 目录DeepSeek 介绍Spring AI 是什么?1、环境准备2、构建项目2.1、pom依赖2.2