JVM汇总

发布于:2025-07-12 ⋅ 阅读:(10) ⋅ 点赞:(0)

1.什么是JVM?

Java虚拟机,Java具有自动内存管理等一系列特性,为实现Java跨平台,一次编译处处执行。

2.JVM结构图

3.类加载器-入口

加载class文件,将类信息存放到运行时数据区的方法区内存空间中

通过魔数和文件格式来判断是否是class文件

类生命周期或加载过程

3.1 类加载器分类

  • 启动类加载器(BootstrapClassLoader):由C++实现。

  • 扩展类加载器(ExtCla ssLoader/PlatformClassLoader):由Java实现,派生自ClassLoader类。

  • 应用程序类加载器(AppClassLoader):也叫系统类加载器。由Java实现,派生自ClassLoader类。

  • 自定义加载器 :程序员可以定制类的加载方式,派生自ClassLoader类。

3.2 双生委派机制

类加载器收到加载请求,不是自己尝试加载,请求委托父加载器,依次向上,自底向上避免重复加载,自顶向下进行加载,避免核心类被修改。

沙箱安全机制:包含核心类(启动类,扩展类加载器中的类不被破坏),防止内存中出现多份同样的字节码。

4. 运行时数据区

4.1 方法区/非堆

被所有线程锁共享{共享区间},类信息{接口、方法}+静态变量+常量+运行时常量池。

4.2 堆

存放指向类元数据的地址,JVM启动时创建,堆内存大小可以调节。

-Xms 512m 初始大小

-Xmx 512m 最大大小

非堆内存

# 设置元空间大小(JDK 8+ 替代永久代)
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m

# 设置线程栈大小
-Xss1m  # 每个线程栈大小为 1MB

4.2.1 分代

堆内存分为:新生代/老年代/永久代(在方法区,8之后变成元空间,只是逻辑上属于堆内存)

年轻代分为:伊甸园区,幸存者区

4.2.2 分代空间工作流程

频繁的垃圾回收成为Minor GC

1.生命周期短的对象,在新生代创建,在新生代被垃圾回收。

2.生命周期长的对象,在新生代创建,老年代回收

3.几乎所以对象创建在伊甸园区,大多销毁在新生代,大对象直接进入老年代

先创建在伊甸园区,满后触发垃圾回收器,回收不再被引用的对象,将剩余对象移动到幸存者0区,对象赋值年龄计数器为1。伊甸园区清空

再次满后,对伊甸园区和幸存者0区进行销毁不再被引用的对象,将剩余对象都移动到幸存者1区

对应对象的年龄计数器+1

依次类推,幸存者区0和1进行交替存放,对应对象年龄+1,年龄达到15晋升老年代

4.2.3 堆空间分配

新生代1/3堆空间,老年代2/3,新生代:8:1:1

老年代满后产生Major GC,触发Full GC 进行老年代垃圾回收。清理后仍然不能进行对象保存此时产生OOM异常

4.2.4 永久代

存放运行环境必须的类信息,fullgc触发回收,若出现OOM:PermGen,永久代设置内存不足被占满

Jdk 自身携带的class interface 等

jdk1.8 去永久代变元空间,元空间占用的是本地物理内存

4.2.5 内存溢出的时候:如何分析

内存溢出分析工具

MAT

eclipse推出的内存分析利器,支持解析hprof等格式的堆快照,能快速定位大对象、内存泄漏点,适合处理GB级快照文件

jvisualvm

通过OQL(对象查询语言)在堆快照中查询特定对象(如“所有未被回收的String对象”),精准定位内存问题。

arthas 阿尔萨斯 https://arthas.aliyun.com/doc/quick-start.html

JVM

启动 java -jar arthas-boot.jar

按 Java 进程前面的 序号,然后回车

会展示当前进程的信息 dashboard

查看静态的属性 getstatic

查看当前 JVM 的信息 jvm

查看内存使用状态 memory

常规命令

会打印线程 ID 的栈 thread ID

反编译类 jad

监控方法的返回值 watch

退出 quit、exit

4.2.6 垃圾回收算法

复制算法

堆内存分割两块,遍历from空间检索存活对象搬运到to空间,清理from空间,名称互换

实现简单,不存在内存碎片。内存缩小一半

标记清除

使用可达性分析算法,标记可达对象,清除未标记对象。

充分利用内存。经过两次扫描,产生内存碎片。

标记整理/标记压缩

在标记清除基础上进行压缩空间。

充分利用内存,不会产生内存空间。经过两次扫描耗费时间,存活对象对整理麻烦,算法效率降低

分代收集算法

三合一,年轻代复制算法,老年代标记混合实现。

4.2.7 判断一个对象是否可回收

引用计数法

可达性分析算法

4.3 栈

线程创建时创建,声明周期跟随线程,线程私有,线程上执行的每个方法都各自对应一个栈帧。

栈存储栈帧,栈帧是一个内存区块,包含方法执行的数据信息

4.3.1 局部变量表

存储方法参数和方法体内定义的局部变量:8种基本类型变量、对象引用变量、实例方法。

4.3.2 操作数栈

在方法执行过程中根据字节码指令记录当前操作的数据,将它们入栈或出栈。用于保存计算过程的中间结果,同时作为计算过程中变量的临时存储空间。

4.3.3 动态链接

可以知道当前帧执行的是哪个方法。指向运行时常量池中方法的符号引用。程序真正执行时,类加载到内存中后,符号引用会换成直接引用

4.3.4 方法返回地址

可以知道调用完当前方法后,上一层方法接着做什么,即“return”到什么位置去。存储当前方法调用完毕

4.3.5 栈溢出

StackOverflowError

出现情况:代码循环调用,栈空间不足 -Xss2M

垃圾回收不涉及栈内存。方法调用结束后自动弹出

方法内的局部变量线程安全吗?

当方法内局部变量没有逃离方法的作用范围时线程安全,因为一个线程对应一个栈,每调用一个方法就会新产生一个栈桢,都是线程私有的局部变量,当变量是static时则不安全,因为是线程共享的。

4.4 本地方法栈

存储本地方法地址

4.5 程序计数器

每个线程都有,线程私有,运行时的指针,存储指向下一条命令的地址

5.本地方法接口

本地方法存储了从Java代码中调用本地方法时所需的信息。线程私有。

5.1 本地方法

由native修饰,c/c++编写,通过Java编译器看不到

5.2 本地方法库

提供所有本地方法与接口的实现

6. 执行引擎-出口

负责解析命令,交给操作系统执行

6.1 解释器

Java字节码加载到内存中,解释器逐条解析和执行字节码指令转换对应平台的本地机器指令。无需等待编译,执行速度较慢。

6.2 即时编译器

为了提高执行速度,JVM还使用即时编译器。即时编译器将字节码动态地编译为本地机器码,以便直接在底层硬件上执行。


网站公告

今日签到

点亮在社区的每一天
去签到