news 2026/5/22 5:43:54

JVM笔记

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JVM笔记

JVM

  • 1 JVM内存模型
    • 1.1 类装载器ClassLoader
    • 1.2 程序计数器(Program Counter Register)
    • 1.3 虚拟机栈(Java Virtual Machine Stacks)
    • 1.4 本地方法栈(Native Method Stacks)
    • 1.5 方法区
    • 1.6 堆
    • 1.7 jvm参数
  • 2 对象创建
    • 2.1 给对象分配内存
    • 2.2 线程安全性问题
    • 2.3 初始化对象
      • 2.3.1 对象结构
      • 2.3.2 对象的访问定位
  • 3 垃圾回收
    • 3.1 如何判断对象为垃圾对象
    • 3.2 如何回收
      • 3.2.1 回收策略
      • 3.2.2 垃圾回收器
  • 4 class文件
    • 4.1 class文件简介
      • 4.1.1 类文件结构

1 JVM内存模型

1.1 类装载器ClassLoader

负责加载class文件


BootStrap启动类加载器,C语言实现,加载rt.jar里的所有class(包括Object)
Extension扩展类加载器
APPClassLoader应用程序类加载器,也叫系统类加载器,加载当前应用的classpath的所有类

也可以自定义加载器,只要继承ClassLoader就可以

双亲委派机制
委派给父类ClassLoader去加载,为了保证原始的rt.jar下的代码不被篡改。

  1. AppClassLoader先不去加载,委派给ExtClassLoader去加载,
  2. ExtClassLoader也不去加载,委派给BootStrap加载,
  3. 只有在父类加载不到的时候,才会自己去加载。

反射有几种方式?
三种。

  1. 通过Object类的getClass方法来获取;
  2. 使用.class的方式;
  3. 使用Class.forName方法

native方法(本地方法)
本地接口的作用是融合不同的编程语言为java所用

1.2 程序计数器(Program Counter Register)

是JVM中一块较小的内存区域
保存着当前线程所执行的字节码的行号
如果正在执行的是native方法,这个计数器的值为undefined。

JVM的多线程是通过线程轮流切换并分配CPU执行时间片的方式来实现的,任何一个时刻,一个CPU都只会执行一条线程中的指令。为了保证线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各线程间的程序计数器独立存储,互不影响。

此区域是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域,因为程序计数器是由虚拟机内部维护的,不需要开发者进行操作。

1.3 虚拟机栈(Java Virtual Machine Stacks)

存放

  1. 本地变量(8中基本类型,对象的引用变量+实例方法):输入输出参数、方法中的变量
  2. 栈操作:记录入栈、出栈操作

是线程隔离的,每创建一个线程时就会对应创建一个Java栈,即每个线程都有自己独立的虚拟机栈。

栈帧包含局部变量表、操作数栈、动态链接、方法返回地址等信息,每一个方法从调用到最终返回结果的过程,就对应一个栈帧从入栈到出栈的过程。

当前栈帧:栈顶的栈帧才是有效的,与这个栈帧相关联的方法称为当前方法,当前活动帧栈始终是虚拟机栈的栈顶元素。

局部变量表:存放了编译期可知的各种基本数据类型和对象引用类型。通常我们所说的“栈内存”指的就是局部变量表这一部分。
局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧分配多少内存是固定的,运行期间不会改变局部变量表的大小。
64位的long和double类型的数据会占用2个局部变量空间,其余的数据类型只占用1个。

1.4 本地方法栈(Native Method Stacks)

与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。

1.5 方法区

存放类/接口信息+常量+静态变量+运行时常量区,是一种定义/接口,一种规范。

其中类信息包含包名、类名、继承的对象、接口定义、方法定义、类变量、类常量

常量池( Constant Pool )是方法区的一部分 Class 文件除了有类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池,这部分内容将在类加载后进入方法区的运行时常量池中存放。

永久带(jdk7)/元空间(jdk8)是方法区的实现。

不会被垃圾回收,关闭jvm时释放此区域所占内存。方法区溢出报错java.lang.OutOfMemoryError: PermGen space,说明程序启动时需要加载大量第三方jar包,例如在一个tomcat下不熟太多应用。可以增加该区域内存空间。

Jdk1.6 及之前: 有永久代 , 常量池 1.6 在方法区
Jdk1.7 有永久代,但已经逐步“去永久代”,常量池 1.7 在堆
Jdk1.8 及之后: 无永久代,常量池 1.8 在元空间

1.6 堆

类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息。

小对象内存分配:先进栈上分配,然后是eden区
大对象直接进如老年代
S0区线程区
YoungGC只会回收eden

1.7 jvm参数

-Xms设置堆的最小空间大小。
-Xmx设置堆的最大空间大小。
-Xmn:设置年轻代大小
-XX:NewSize设置新生代最小空间大小。
-XX:MaxNewSize设置新生代最大空间大小。
-XX:PermSize设置永久代最小空间大小。
-XX:MaxPermSize设置永久代最大空间大小。
-Xss设置每个线程的堆栈大小
-XX:+UseParallelGC:选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集。
-XX:ParallelGCThreads=20:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。
典型JVM参数配置参考:

java-Xmx3550m-Xms3550m-Xmn2g-Xss128k
-XX:ParallelGCThreads=20
-XX:+UseConcMarkSweepGC-XX:+UseParNewGC
-Xmx3550m:设置JVM最大可用内存为3550M。

-Xms3550m:设置JVM促使内存为3550m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。

-Xmn2g:设置年轻代大小为2G。整个堆大小=年轻代大小+年老代大小+持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,官方推荐配置为整个堆的3/8。

-Xss128k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大 小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000 左右。

2 对象创建

2.1 给对象分配内存

  1. 指针碰撞
  2. 空间列表

2.2 线程安全性问题

  1. 线程同步(性能较差,同步开销)
  2. 本地线程分配缓冲(TLAB)

2.3 初始化对象

2.3.1 对象结构

  1. Header( 对象头)
  • 自身运行时数据( Mark Word)
    – 哈希值
    – GC 分代年龄
    – 锁状态标志
    – 线程持有锁
    – 偏向线程 ID
    – 偏向时间戳
  • 类型指针
  • 数组长度(只有数组对象才有)
  1. InstanceData
    相同宽度的数据分配到一起( long,double
  2. Padding (对齐填充
    8 个字节的整数倍

2.3.2 对象的访问定位

  1. 使用句柄
    好处:如果堆中的对象内存地址变更之后,句柄的指向变更就可以了,栈中对象的引用不需要改变
    缺点:性能
  2. 直接指针(HotSpot)

3 垃圾回收

3.1 如何判断对象为垃圾对象

引用计数法、可达性分析

  1. 引用计数法
    在对象中添加一个引用计数器,当有地方引用这个对象的时候,计数器 +1 ,当失效的时候,计数器 -1
    verbose:gc XX:+PrintGCDetails
  2. 可达性分析


GCRoot 的对象

  • 虚拟机栈 局部变量表中的
  • 方法区的类属性所引用的对象
  • 方法区的常量所引用的对象
  • 本地方法栈所引用的对象

3.2 如何回收

3.2.1 回收策略

标记清除、复制、标记整理、分代算法

  1. 标记清除
    分成标记和清除两个阶段
    缺点:效率问题、内存碎片

  2. 复制
    缺点:空间利用率低、只能用一般的空间


  • 新生代(Eden、Survivor)
    老年代(Tenured Gen)

  • 方法区

  • 虚拟机栈

  • 本地方法栈

程序计数器
4. 标记整理
5. 分代算法

3.2.2 垃圾回收器

Serial、ParNew、CMS、G1、ZGC

4 class文件

public static void main(String args){ int a = 2; int b = 400; int c = a+b; System.out.println(c); }

用javap命令反编译后

public static void main(java.lang.String); descriptor: (Ljava/lang/String;)V //L代表引用类型 V代表没有返回值 flags: ACC_PUBLIC,ACC_STATIC //方法描述符 code: stack=2,locals=4,args_size=1 //操作数栈深度为2,本地变量表最大长度4(单位slot),参数1个 0: iconst_2 //常量2压栈 1: istore_1 //常量2出栈并保存到变量1里 2: iconst_3 //常量400压栈 3: istore_2 //常量400出栈并保存到变量2里

接上

4.1 class文件简介

https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html
class文件是一组已8位字符为基础单位的二进制流,没有任何分隔符,节省空间,执行效率高。
class文件只有两种数据类型:无符号数、表

4.1.1 类文件结构

ClassFile { u4 magic; //魔数 u2 minor_version; //jdk版本号 8对应52 7对应51 类推 u2 major_version; //jdk版本号 u2 constant_pool_count; cp_info constant_pool[constant_pool_count-1]; u2 access_flags; u2 this_class; u2 super_class; u2 interfaces_count; u2 interfaces[interfaces_count]; u2 fields_count; field_info fields[fields_count]; u2 methods_count; method_info methods[methods_count]; u2 attributes_count; attribute_info attributes[attributes_count];

}
魔数:代表了文件类型cafe babe

java、grovy、jruby、scala都可以编译成class文件。这些语言只需要增加对应的编译器,按照class规范翻译成class文件。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/22 5:43:53

vLLM-v0.17.1效果展示:多LoRA热切换,支持10+垂类模型动态加载

vLLM-v0.17.1效果展示:多LoRA热切换,支持10垂类模型动态加载 1. vLLM框架核心能力 vLLM是一个专为大型语言模型(LLM)设计的高性能推理和服务库,最初由加州大学伯克利分校的天空计算实验室开发,现已发展成为社区驱动的开源项目。…

作者头像 李华
网站建设 2026/5/22 5:43:39

Java中的synchronized和锁

前几天面试时被问到了Java中的自旋锁、轻量锁等,我只略有印象,似乎在哪看见过,但是说不上来,面试结束后就开始搜索,现在的感觉就是——以我工作10余年的经验来看,是否知道这些这么底层的东西对于实际工作来…

作者头像 李华
网站建设 2026/5/2 21:35:46

Chord视频分析工具保姆级教程:结果输出区JSON格式与字段详解

Chord视频分析工具保姆级教程:结果输出区JSON格式与字段详解 1. 工具概述与核心价值 Chord视频时空理解工具是一款基于先进多模态架构的本地智能视频分析解决方案。这个工具最大的特点是能够像人一样理解视频内容,不仅能描述画面里发生了什么&#xff…

作者头像 李华