跳至主要內容

EkkoSonya's Blog

好好学习,天天向上

JVM 八股1 - 整体架构

JVM

学习 JVM 可以帮助我们开发者更好地优化程序性能、避免内存问题

  • 了解 JVM 的内存模型和垃圾回收机制,可以帮助我们更合理地配置内存、减少 GC 停顿
  • 掌握 JVM 的类加载机制可以帮助我们排查类加载冲突或异常
  • JVM 还提供了很多调试和监控工具,可以帮助我们分析内存和线程的使用情况,从而解决内存溢出内存泄露等问题

所以主要内容:内存模型、垃圾回收机制、类加载机制、JVM 调试/监控 工具

什么是 JVM


codejavajvm八股大约 7 分钟
JVM 八股2 - 内存管理1 (内存区介绍)

JVM 启动后,大致可以分为三个功能区:类加载器、运行时数据区以及执行引擎

内存管理

内存区其实是功能区中"运行时数据区"的具体细分

JVM(功能区视角)
│
├── 类加载器
│
├── 运行时数据区  ←──── 这部分就是"内存区"
│   ├── 程序计数器
│   ├── 虚拟机栈
│   ├── 本地方法栈
│   ├── 堆
│   └── 方法区
│
└── 执行引擎
    ├── JIT 编译器
    └── 垃圾回收器

codejavajvm八股大约 8 分钟
JVM 八股3 - 内存管理2 (native 介绍)

JVM3

当 Java 应用需要与操作系统底层或硬件交互时,通常会用到本地方法栈。

native 方法

native 方法是在 Java 中通过 native 关键字声明的,用于调用非 Java 语言,如 C/C++ 编写的代码。Java 可以通过 JNI,也就是 Java Native Interface 与底层系统、硬件设备、或者本地库进行交互

Java 的 native 方法本身没有 Java 代码实现,而是交给本地代码实现

public native void test();

codejavajvm八股大约 5 分钟
JVM 八股4 - 内存管理3

虚拟机栈、本地方法栈、程序计数器 | 堆、方法区

不同 JDK 下内存区域变化

主要是方法区实现的不同

方法区是 JVM 规范里的概念, JVM 需要有这么一块区域来存类相关信息

HotSpot 实现层面:曾经用永久代、后来用元空间来实现

JDK 1.6 使用永久代来实现方法区:

alt text

JDK 1.7 时仍然是永久带,但发生了一些细微变化,比如将字符串常量池、静态变量存放到了堆上。


codejavajvm八股大约 11 分钟
OS5 - 进程管理3

OS

Java 线程状态

Java 没有 Ready 状态,线程状态有 6 种:

NEW              新建
RUNNABLE         可运行(包含 Ready + Running)
BLOCKED          阻塞(等待锁)
WAITING          等待(wait/join/park 等)
TIMED_WAITING    限时等待
TERMINATED       终止

codejavaCS大约 2 分钟
OS6 - 进程管理4

OS

中断切换

以线程 A 发起读取磁盘文件为例

第一阶段:发起 I/O 时的“软中断”与主动切换(线程 A 交出 CPU)

当线程 A 执行到读取文件的代码时,它其实是没有权限直接控制底层硬件的,必须向操作系统老大哥(内核)求助。

  • 系统调用(软中断/Trap): 线程 A 调用 read() 函数,这会触发一个软中断(在 x86 架构下通常是 int 0x80 指令或 syscall 指令)。CPU 收到这个信号后,会从“用户态”切换到“内核态”,开始执行操作系统的代码。
  • 下达指令与阻塞: 操作系统内核接收到请求,把读取任务交给 DMA 和磁盘控制器。因为此时数据还没准备好,操作系统会将线程 A 的状态标记为 BLOCKED(阻塞状态),并把它移出 CPU 的运行队列。
  • 第一次上下文切换(Context Switch): 既然线程 A 被挂起了,CPU 不能闲着。操作系统的调度器(Scheduler)会立刻介入,从就绪队列里挑出一个线程 B,把 CPU 的控制权交给它。
    • 此时,DMA 在默默搬运硬盘数据,而 CPU 正在全速运行线程 B 的代码。两者互不干扰。

codejavaCS大约 14 分钟
Java面试题 - java基础9 (lambda闭包)

Java 基础

我在主线程创建了一个锁对象,然后对应的子线程为什么可以得到这个锁

在主线程中通过 new 关键字(例如 new Object() 或 new ReentrantLock())创建一个锁对象时,这个对象是被分配在 JVM 的**堆(Heap)**内存中的

在 Java 内存模型中,每个线程有自己私有的虚拟机栈(用于存储局部变量表、方法出口等),但堆内存是所有线程共享的。只要子线程能够拿到这个对象的内存地址(引用),它就可以访问这个对象。

传递的是对象引用

当主线程启动子线程时,通常会将这个锁对象的引用(相当于这把锁在堆内存中的真实地址)通过某种方式传递给子线程:


codejava大约 4 分钟
JUC 八股18 - 并发工具类 (HashTable)

并发工具类

HashTable

alt text
  • Hashtable的底层数据结构主要是数组加上链表,数组是主体,链表是解决hash冲突存在的。

  • HashTable是线程安全的,实现方式是Hashtable的所有公共方法均采用synchronized关键字,当一个线程访问同步方法,另一个线程也访问的时候,就会陷入阻塞或者轮询的状态。

原理

本质就是构建了一个存储了 Entry<?,?> 对象的数组,数据就存放在具体的 Entry 对象里,对应的索引根据 hash 来算


codejavajuc八股大约 7 分钟
JUC 八股20 - 并发工具类

并发工具类

作用
Semaphore 限制线程的数量
Exchanger 两个线程交换数据
CountDownLatch 线程等待直到计数器减为 0 时开始工作
CyclicBarrier 作用跟 CountDownLatch 类似,但是可以重复使用
Phaser 增强的 CyclicBarrier

codejavajuc八股大约 5 分钟
JUC 八股20 - 线程池1(阻塞队列)

线程池

阻塞队列

常用的有五种

  • 有界队列 ArrayBlockingQueue;
  • 无界队列 LinkedBlockingQueue;
  • 优先级队列 PriorityBlockingQueue;
  • 延迟队列 DelayQueue;
  • 同步队列 SynchronousQueue
alt text

阻塞队列接口 BlockingQueue<E>


codejavajuc八股大约 2 分钟