JVM实现
计算机知识思维导图:JVM实现。网页展示前三层结构,可在线查看完整脑图并下载 GoMind 文件。
2026-08-25
## JVM实现 ### JVM实现 #### JVM ##### oracle ##### IBM ##### 参考 #### JVM有4垃圾回收器 ##### Serial Collector[默认]:序列垃圾回收器 ##### Parallel Collector:并发垃圾回收器 ##### Parallel Compacting Collector:并行压缩垃圾回收器 ##### Concurrent Mark-Sweep (CMS) Collector:并发标志清除垃圾回收器 #### 配置 ##### \-Xms128m #JVM初始分配的堆内存 ##### \-Xmx512m #JVM最大允许分配的堆内存,按需分配 ##### \-XX:PermSize=64M #JVM初始分配的非堆内存 ##### \-XX:MaxPermSize=128M #JVM最大允许分配的非堆内存,按需分配 ##### 远程调试 ##### jconsole,VisualVM,JMC 监控配置(rmi) ##### 记录下服务器历史的GC ##### jvm shut down时,输出dump文件 ##### JVM 内存区域大小参数设置 #### jvm诊断 ##### jinfo(运行环境) ##### 栈 ##### 堆 ##### 工具 ## jvm-虚拟机结构 ### jvm-虚拟机结构 #### 共识 ##### javap 查看编译后的 java class 文件字节码 ##### 栈 ##### 每个线程会都有自己私有的java虚拟机栈,这个栈与线程同时创建 #### 运行时数据区 ##### 线程私有 ##### 线程共享 #### 参考 ##### Java虚拟机规范(Java SE 7).pdf ##### JVM内幕:Java虚拟机详解 - ImportNew ##### 运行时常量池和包装类常量池有什么区别? - 知乎 ##### ★ JVM内存模型 ##### 常量池_百度百科 ## HotSpot垃圾收集器 ### HotSpot垃圾收集器 #### 共识 ##### 并行(Parallel):多条垃圾收集线程并行工作,而用户线程仍处于等待状态 ##### 并发(Concurrent):垃圾收集线程与用户线程一段时间内同时工作(交替执行) ##### Stop-The-World ##### OOM(out of memory)内存不足 ##### GC时机与相关因素 #### 分待收集器 ##### 新生代收集器 ##### 老年代收集器 ##### 算法-复制算法&标记-整理 #### 配置 ##### \-XX 参数被称为不稳定参数 #### 参考 ##### JVM(HotSpot) 垃圾收集器 - 简书 ##### JVM 内存区域大小参数设置 - 简书 ##### JVM垃圾回收算法 ## 回收器算法 ### 回收器算法 #### 收集垃圾 ##### 引用计数器算法 ##### 根搜索算法 #### 回收算法 ##### 标记-清除算法 ##### 复制算法 ##### 标记-整理算法 #### 参考 ##### 深入理解JVM--JVM垃圾回收机制 ##### JVM垃圾回收算法 ## 类加载过程 ### 类加载过程 #### 共识 ##### 类加载与动态代理 #### 过程 ##### 类声明周期分为:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、卸御(Unloading) ##### 加载 ##### 连接阶段 ##### 类型初始化 ##### 使用 ##### 卸载 #### JVM中的类加载器 ##### 启动(引导)类加载器(BootStrap ClassLoader) ##### 扩展类加载器(Extension ClassLoader) ##### 系统类加载器(System ClassLoader) ##### 双亲委派模型(自启动-扩展-系统-自定义) ##### 自定义类加载器 #### 参考 ##### JVM类加载机制 - 推酷 ##### JVM 类加载机制深入浅出 > 仅展示前三层结构;请在线查看完整脑图或下载 GoMind 文件。
JVM实现
JVM实现
Sun JDK和OpenJDK中所带的虚拟机
HotSpot VM
BEA公司
JRockit VM
oracle
IBM JVM
IBM
什么是HotSpot VM & 深入理解Java虚拟机 JVM - blcblc - 博客园
三大Java 虚拟机垃圾回收机制的比较(HotSpot, JRockit, IBM JVM)
参考
JVM
Serial Collector[默认]:序列垃圾回收器
Parallel Collector:并发垃圾回收器
Parallel Compacting Collector:并行压缩垃圾回收器
Concurrent Mark-Sweep (CMS) Collector:并发标志清除垃圾回收器
JVM有4垃圾回收器
-Xms128m #JVM初始分配的堆内存
-Xmx512m #JVM最大允许分配的堆内存,按需分配
-XX:PermSize=64M #JVM初始分配的非堆内存
-XX:MaxPermSize=128M #JVM最大允许分配的非堆内存,按需分配
Jdk1.7之前: -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n
jdk1.7之后: -agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=n
远程调试
jconsole,VisualVM,JMC 监控配置(rmi)
记录下服务器历史的GC
jvm shut down时,输出dump文件
-XX:MaxTenuringThreshold=15
如果设置为0的话,则新生代对象不经过Survivor区,直接进入老年代。对于老年代比较多的应用,可以提高效率
如果将此值设置为一个较大值,则新生代对象会在Survivor区进行多次复制,这样可以增加对象再新生代的存活时间,增加在新生代即被回收的概论
设置新生代代对象进入老年代的年龄
JVM 内存区域大小参数设置
配置
jstat -gc(GC监视)
jinfo(运行环境)
jps(java进程)/jstack(线程状态)同Kill -3
栈
jmap -heap(堆信息)/jmap -histo:live(对象内存分布)/jmap -dump(内存镜像)
历史gc过程留存 -Xloggc:webapps/sys/gc.log
GC
堆
jconsole ,VisualVM,JMC,jprofile|GCViewer
工具
jvm诊断
jvm-虚拟机结构
jvm-虚拟机结构
javap -v ClassName.class
javap 查看编译后的 java class 文件字节码
后进先出(Last-In-First-Out,LIFO)
当前方法未出栈,调用新方法,会创建新栈
压栈
栈
同时会映射到操作系统中的线程
每个方法的调用和返回,同时创建一个栈帧
每个线程会都有自己私有的java虚拟机栈,这个栈与线程同时创建
共识
调用参数传递 / 局部变量
存储:索引,下标0开始
局部变量表(或数组)
(本地方法栈)
操作数栈的长度由编译期决定,并且存储于类和接口的二进制表示之中
从局部变量表或者对象实例的字段中复制常量或变量值到操作数栈中
从操作数栈取走数据、操作数据和把操作结果重新入栈
Untitled node
SayHellp(){System.out.println("Hello")} 示例
字节码指令
在方法调用的时候,操作数栈也用来准备调用方法的参数以及接收方法返回结果
方法调用
Integer在编译中优化了代码.使用int自动封箱成了Integer
int类型的字面值
CONSTANT_Integer
float类型的字面值
CONSTANT_Float
long类型的字面值
CONSTANT_Long
double类型的字面值
CONSTANT_Double
各种基本类型字面值(如int、long等等)
CONSTANT_String
常量池中
对象型(或引用类型)(如String及数组)
字面值
CONSTANT_Methodref,*ref等
符号引用在常量池中
"引用实例"在堆中,通过动态链接完成"直接引用"
文本形式出现的符号引用
其它(非操作数栈)
基础类型的值会直接保存在操作数栈中[int,long,char..]
操作数栈
Untitled node
可能Start为下一指令的位置
LocalVariableTable中Start[0,3,6]与Name[args,s1,s2]关系
可能是值得复用 #2
Length[s1,s2]为何不一致
疑问
示例
public class TestJvm {
public static void main(String[] args) {
// int i = 998;
String s1 = "hello";
String s2 = "hello";
}
}源码行与操作数栈的对应关系
支持当前方法的代码实现动态链接(Dynamic Linking)
类当前方法的运行时常量池引用
在 Class 文件里面,描述一个方法调用了其他方法,或者访问其成员变量是通过符号引用(Symbolic Reference)来表示
动态链接的作用就是将这些符号引用所表示的方法转换为实际方法的直接引用
动态链接
返回[return/exception]
栈帧
java虚拟机栈
每一条 Java虚拟机线程都有自己的 PC(Program Counter)寄存器
正在被线程执行的方法称为该线程的当前方法(Current Method)
PC 寄存器就保存 Java 虚拟机正在执行的字节码指令的地址
程序计数器/PC寄存器
本地方法区
线程私有
Classloader 引用
数值型常量
字段引用
方法引用
属性
内容
class文件每一个类或接口的常量池表(constant_pool table)的运行时表现形式
"运行时常量池引用"中会指向具体的对象在堆中的地址
常量池中的"符号引用"类utf8,会对应目标对象类型
运行时常量池
Untitled node
类信息:签名,版本
Untitled node
相同Utf8值,将复用
示例
public class TestJvm {
public static void main(String[] args) {
// int i = 998;
String s1 = "hello";
String s2 = "hello";
}
}构造方法
<init>方法
静态方法
<clinit>方法
初始化方法 [<init>,<clinit>]
方法实体
()V
第一个值(“.”号之前的)指向类实体
第二个值指向 NameAndType 实体
<类实体> . <NameAndType>
Methodref,*ref等
值1 : 指向一个 Utf8 字符串实体,它是一个方法名或者字段名
值2 : 指向表示类型的 Utf8 实体。对于字段类型,这个值是类的全名,对于方法类型,这个值是每个参数类型类的类全名的列表
<目标utf8名> : <目标类型utf8实体>
NameAndType
表示说明
int类型的字面值
CONSTANT_Integer
float类型的字面值
CONSTANT_Float
long类型的字面值
CONSTANT_Long
double类型的字面值
CONSTANT_Double
各种基本类型(如int、long等等)
String类型字面值的引用
CONSTANT_String
对象型(或引用类型)(如String及数组)
字面量
对一个类或接口的符号引用
CONSTANT_Class
Untitled node
对一个字段的符号引用
CONSTANT_Fieldref
第一个值(“.”号之前的)指向类实体
第二个值指向 NameAndType 实体
当前类对象的初始化方法(构造), <类实体> . <NameAndType>
对一个类中方法的符号引用
CONSTANT_Methodref
对一个接口中方法的符号引用
CONSTANT_InterfaceMethodref
值1 : 指向一个 Utf8 字符串实体,它是一个方法名或者字段名
值2 : 指向表示类型的 Utf8 实体。对于字段类型,这个值是类的全名,对于方法类型,这个值是每个参数类型类的类全名的列表
方法执行关系, <目标utf8名> : <目标类型utf8实体>
对一个字段或方法的部分符号引用
CONSTANT_NameAndType
文本形式出现的符号引用(如static/对象引用/new等)
UTF-8编码方式来表示程序中所有的重要常量字符串
CONSTANT_Utf8
#22 = Utf8 hello
#24 = Utf8 java/lang/Object
⑥字符串字面值
字面量
#5 = Utf8 <init>
#6 = Utf8 ()V
?
符号引用
#11 = Utf8 LTestJvm
①类或接口的全限定名
②超类的全限定名
③父接口的全限定名
限定名
#12 = Utf8 main
⑤类方法名和返回类型名、以及参数名和所属类型名
#13 ~ #18
④类字段名和所属类型名
#7 = Utf8 Code
源码行与操作数栈的对应关系
#8 = Utf8 LineNumberTable
局部变量表(或数组)[信息]
#9 = Utf8 LocalVariableTable
#10 = Utf8 this
#19 = Utf8 SourceFile
#20 = Utf8 TestJvm.java
#23 = Utf8 TestJvm
暂未知
其它
Constant_utf8分类
```
public class TestJvm {
public static void main(String[] args) {
// int i = 998;
String s1 = "hello";
String s2 = "hello";
}
}
```
```
G:\liuxiang_code_git\myServer\@jvm\demo-jvm\target\classes>javap -v TestJvm.class
Classfile /G:/liuxiang_code_git/myServer/@jvm/demo-jvm/target/classes/TestJvm.class
Last modified 2017-7-2; size 440 bytes
MD5 checksum c732babf69bf7bb7012d9b81a40544b7
Compiled from "TestJvm.java"
public class TestJvm
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #4.#21 // java/lang/Object."<init>":()V
#2 = String #22 // hello
#3 = Class #23 // TestJvm
#4 = Class #24 // java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Utf8 Code
#8 = Utf8 LineNumberTable
#9 = Utf8 LocalVariableTable
#10 = Utf8 this
#11 = Utf8 LTestJvm;
#12 = Utf8 main
#13 = Utf8 ([Ljava/lang/String;)V
#14 = Utf8 args
#15 = Utf8 [Ljava/lang/String;
#16 = Utf8 s1
#17 = Utf8 Ljava/lang/String;
#18 = Utf8 s2
#19 = Utf8 SourceFile
#20 = Utf8 TestJvm.java
#21 = NameAndType #5:#6 // "<init>":()V
#22 = Utf8 hello
#23 = Utf8 TestJvm
#24 = Utf8 java/lang/Object
{
public TestJvm();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LTestJvm;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=3, args_size=1
0: ldc #2 // String hello
2: astore_1
3: ldc #2 // String hello
5: astore_2
6: return
LineNumberTable:
line 5: 0
line 6: 3
line 7: 6
LocalVariableTable:
Start Length Slot Name Signature
0 7 0 args [Ljava/lang/String;
3 4 1 s1 Ljava/lang/String;
6 1 2 s2 Ljava/lang/String;
}
SourceFile: "TestJvm.java"
```"运行时常量池引用"中会指向具体的对象在堆中的地址
"符号引用"类utf8,会对应目标对象类型
常量字符串(如Utf8)
Untitled node
static
类型
(字节码)常量池
Untitled node
示例
操作数栈[信息]
Code-stack
源码行与操作数栈的对应关系
Code-LineNumberTable
局部变量表(或数组)[信息]
Code-LocalVariableTable
LocalVariableTable(局部变量表) -> stack(操作数帧) -> Constant pool(常量池)
关系
各方法信息
javap -v ClassName.class
查看方法区内容
方法区
驻留字符串(interned strings)
iload、iload_<n>、lload、lload_<n>、fload、fload_<n>、dload、dload_<n>、aload、aload_<n>
局部变量加载到操作栈的指令
istore、istore_<n>、lstore、lstore_<n>、fstore、fstore_<n>、dstore、dstore_<n>、astore、astore_<n>
从操作数栈存储到局部变量表的指令
bipush、sipush、ldc、ldc_w、ldc2_w、aconst_null、iconst_m1、iconst_<i>、lconst_<l>、fconst_<f>、dconst_<d>
常量加载到操作数栈
扩充局部变量表的访问索引的指令:wide
加载和存储指令
加法指令:iadd、ladd、fadd、dadd
减法指令:isub、lsub、fsub、dsub
乘法指令:imul、lmul、fmul、dmul
除法指令:idiv、ldiv、fdiv、ddiv
求余指令:irem、lrem、frem、drem
取反指令:ineg、lneg、fneg、dneg
位移指令:ishl、ishr、iushr、lshl、lshr、lushr
按位或指令:ior、lor
按位与指令:iand、land
按位异或指令:ixor、lxor
局部变量自增指令:iinc
比较指令:dcmpg、dcmpl、fcmpg、fcmpl、lcmp
运算指令
宽化类型转换(Widening Numeric Conversions,小范围类型向大范围类型的安全转换)
i2b、i2c、i2s、l2i、f2i、f2l、d2i、d2l 和 d2f
窄化类型转换(Narrowing Numeric Conversions)
类型转换指令
创建类实例的指令:new
创建数组的指令:newarray,anewarray,multianewarray
访问类字段(static 字段,或者称为类变量)和实例字段(非 static 字段,或者成为实例变量)的指令:getfield、putfield、getstatic、putstatic
把一个数组元素加载到操作数栈的指令:baload、caload、saload、iaload、laload、faload、daload、aaload
将一个操作数栈的值储存到数组元素中的指令:bastore、castore、sastore、iastore、fastore、dastore、aastore
取数组长度的指令:arraylength
检查类实例类型的指令:instanceof、checkcast
对象创建与操作
pop、pop2、dup、dup2、dup_x1、dup2_x1、dup_x2、dup2_x2 和 swap
操作数栈管理指令
条件分支:ifeq、iflt、ifle、ifne、ifgt、ifge、ifnull、ifnonnull、if_icmpeq、if_icmpne、if_icmplt, if_icmpgt、if_icmple、if_icmpge、if_acmpeq 和if_acmpne。
复合条件分支:tableswitch、lookupswitch
无条件分支:goto、goto_w、jsr、jsr_w、ret
控制转移指令
invokevirtual 指令用于调用对象的实例方法
invokeinterface 指令用于调用接口方法
invokespecial 指令用于调用一些需要特殊处理的实例方法,包括实例初始化方法、私有方法和父类方法
invokestatic 指令用于调用类方法(static 方法)
而方法返回指令则是根据返回值的类型区分的,包括有 ireturn(当返回值是 boolean、byte、char、short 和 int 类型时使用)、lreturn、freturn、dreturn 和 areturn
还有一条 return 指令供声明为 void 的方法、实例初始化方法、类和接口的类初始化方法使用
方法调用和返回指令
程序中显式抛出异常的操作会由 athrow 指令实现
抛出异常
同步结构都是使用管程(Monitor)来支持的
当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先持有管程,然后再执行方法,最后再方法完成(无论是正常完成还是非正常完成)时释放管程。
Java 虚拟机的指令集中有 monitorenter 和 monitorexit 两条指令来支持 synchronized 关键字的语义
同步
操作数栈,指令解析
二进制表示中的 constant_pool 表被用来构造运行时常量池
5.1 描述 Java 虚拟机如何从类或接口的二进制表示中得到符号引用
5.1 运行时常量池
通过引导类加载器(Bootstrap Class Loader §5.3.1)创建一个初始类(Initial Class)来完成
紧接着,Java 虚拟机链接这个初始类,初始化并调用它的 public void main(String[])方法
初始类会作为命令行参数被提供给虚拟机
5.2 节解释 Java 虚拟机启动时会有怎样的加载、链接和初始化过程
5.2 虚拟机启动
使用引导类加载器来加载类型-使用用户自定义类加载器来加载类型
类加载器会递归加载它的父类
5.3 节详述了类和接口的二进制表示是如何通过类加载器加载并由此创建类和接口
5.3 创建和加载
验证-二进制表示结构上的正确
准备-为类或接口的静态字段分配空间,并用默认值初始化这些字段
对*符号引用的进行解析
是根据运行时常量池的符号引用来动态决定具体的值的过程
首先检查方法引用中的 C 是否类或接口
方法引用解析过程会检查 C 和它的父类中是否包含此方法
方法查找过程也会试图从 C 的父接口中去定位所引用的方法
普通方法解析
解析-(检查符号引用是否可以正常被执行)
访问控制-public/protected/private
方法覆盖
5.4 描述链接过程。
5.4 链接
触发指令:new,getstatic,putstatic或 invokestatic
5.5 节详述类和接口是如何被初始化的。
5.5 初始化
native
5.6 节介绍绑定本地方法的概念
5.6 绑定本地方法实现
某些线程调用 Runtime 类或 System 类的 exit 方法,或是 Runtime 类的 halt 方法
5.7 节会说到 Java 虚拟机的退出时机
5.7 Java 虚拟机退出
在类或接口被链接之前,它必须是被成功地加载过
在类或接口初始化之前,它必须是被成功地验证及准备过
程序的直接或间接行为可能会导致链接发生,链接过程中检查到的错误应该在请求链接的程序处被抛出
必须保证下列几点成立
加载、链接与初始化
永久代
用于编译和存储那些被 JIT 编译器编译成原生代码的方法
代码缓存(Code Cache)
非堆
运行时分配类实例、数组 / 不能在栈上存储数组和对象(含包装类型)
因为栈帧被设计为创建以后无法调整大小。栈帧只存储指向堆中对象或数组的引用。
意义
与局部变量数组(每个栈帧中的)中的原始类型和引用类型不同,对象总是存储在堆上以便在方法结束时不会被移除。对象只能由垃圾回收器移除
回收设计
Eden+s,s
young(年轻) heap
年轻代
old
tenured(终生) heap
老生代
存储
堆
线程共享
运行时数据区
Java虚拟机规范(Java SE 7).pdf
JVM内幕:Java虚拟机详解 - ImportNew
运行时常量池和包装类常量池有什么区别? - 知乎
★ JVM内存模型
常量池_百度百科
参考
HotSpot垃圾收集器
HotSpot垃圾收集器
大停顿
并行(Parallel):多条垃圾收集线程并行工作,而用户线程仍处于等待状态
小停顿
并发(Concurrent):垃圾收集线程与用户线程一段时间内同时工作(交替执行)
简称STW,是在执行垃圾收集算法时,Java应用程序的其他所有线程都被挂起(除了垃圾收集帮助器之外)
Java中一种全局暂停现象,全局停顿,所有Java代码停止,native代码可以执行,但不能与JVM交互
Stop-The-World
堆 java.lang.OutOfMemoryError: java heap space
类过多?序列化类多?
永久代(方法区) java.lang.OutOfMemoryError: PermGen space
方法递归,压栈严重,局部变量表内存消耗
栈 java.lang.StackOverflowError
同时伴有:FULL GC
OOM(out of memory)内存不足
新生代区(Eden)满
-XX:MaxTenuringThreshold=15
如果设置为0的话,则新生代对象不经过Survivor区,直接进入老年代。对于老年代比较多的应用,可以提高效率
如果将此值设置为一个较大值,则新生代对象会在Survivor区进行多次复制,这样可以增加对象再新生代的存活时间,增加在新生代即被回收的概论
设置新生代代对象进入老年代的年龄
minor gc
小于新生代担保(java5以前默认关,java6后默认启)
新生代升到老年代的对象大于老年代剩余空间full gc
老生代区满
CMS[并发标记清除] 碎片造成的剩余空间不足,会降级Serial Old[标记整理]回收(full GC)
System.GC()
堆外内存到达指定大小后,即触发Full GC,默认是64M
非堆:永久代区满
非堆?
full gc
GC时机与相关因素
共识
进行垃圾收集时,必 须暂停其他所有的工作线程(用户线程)
Serial 序列:默认 [单线程串行]
Serial序列的多线程并行版
ParNew [多线程并行]
收集器将尽力保证内存回收花费的时间不超过设定值
-XX:MaxGCPauseMillis 最大暂停时间
大于0小于100的整数,也就是垃圾收集时间占总时间的比率,相当于是吞吐量的倒数
-XX:GCTimeRatio 占总时间比率
目标则是达到一个可控制的吞吐量
吞吐量= 程序运行时间/(程序运行时间 + 垃圾收集时间),虚拟机总共运行了100分钟。其中垃圾收集花掉1分钟,那吞吐量就是99%。
Parallel [多线程并行]
算法-复制
Minor GC:新生代GC,指发生在新生代的垃圾收集动作,所有的Minor GC都会触发全世界的暂停(stop-the-world),停止应用程序的线程,不过这个过程非常短暂。
Major GC/Full GC:老年代GC,指发生在老年代的GC。
JVM:Java Virtual Machine(Java虚拟机)的缩写。
GC时机
minor gc
eden满
Second Survivor不一定能容纳下所有从E和F区copy过来的活跃对象。
为了确保minor gc能够顺利完成,GC需要在年老代中额外保留一块足以容纳所有活跃对象的内存空间。
这个预留操作,就被称之为新生代收集担保(New Generation Guarantee)
为什么要关闭新生代收集担保?
新生代收集担保 HandlePromotionFailure-full gc
gc与非gc时间耗时超过了GCTimeRatio的限制引发OOM,调优诸如通过NewRatio控制新生代老年代比例,通过 MaxTenuringThreshold控制进入老年前生存次数等
回收时机
新生代收集器
Serial Old是Serial收集器的老年代版本
作为CMS收集器的后备预案
Serial Old [单线程串行]
Parallel Old是Parallel Scavenge收集器的老年代版本
Parallel Old [多线程并行]
算法-标记整理
并发-响应优先,并行-吞吐优先
多线程并发[牺牲了吞吐量]
是一种以获取最短回收停顿时间为目标的收集器
标记出GC ROOTS能直接关联到的对象,速度很快
初始标记(CMS initial mark)
GC ROOTS 根搜索算法阶段,会判定对象是否存活
并发标记(CMS concurrenr mark)
为了修正并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录
停顿稍长,但比并发标记阶段要短
重新标记(CMS remark)
并发清除(CMS concurrent sweep)
耗时最长的并发标记和并发清除
收集器线程都可以与用户线程一起工作,所以整体来说,CMS收集器的内存回收过程是与用户线程一起并发执行的
特性
收集过程大致分为4个步骤
并发标记阶段是进行GC ROOTS 根搜索算法阶段,会判定对象是否存活
CMS收集器对CPU资源非常敏感
XX:CMSInitiatingOccupancyFraction 预留多少空间开始做GC
剩余空间不足仍然满足不了用户线程生成对象所需要的空间
full gc
虚拟机将启动后备预案:临时启用Serial Old收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了
无法处理浮动垃圾(垃圾回收的过程中会产生新的垃圾)
比如说大对象,内存空间找不到连续的空间来分配不得不提前触发一次Full GC
-XX:UseCMSCompactAtFullCollection fullGC后碎片清理
-XX:CMSFullGCBeforeCompaction 几次fullGC碎片整理一次
CMS是基于“标记-清除”算法实现的收集器,使用“标记-清除”算法收集后,会产生大量碎片
CMS收集器的优点:并发收集、低停顿,但是CMS还远远达不到完美,器主要有三个显著缺点
CMS(Concurrent Mark Sweep) [多线程并发]
算法-标记清除
老年代收集器
并行与并发,分代收集,空间整合,可预测停顿
复制算法&标记-整理
新生代,老年代都可回收
用在heap memory很大的情况下,把heap划分为很多很多的region块,然后并行的对其进行垃圾回收
在清除实例所占用的内存空间后,还会做内存压缩
一个region有可能属于Eden,Survivor或者Tenured内存区域
用于存储大对象-即大小超过一个region大小的50%的对象
G1垃圾收集器还增加了一种新的内存区域,叫做Humongous内存区域
G1触发minor gc的时候一并将年老代上的Initial Mark给做了
特性
region块,并行与并发,分代收集,空间整合,可预测停顿
初始标记,并发标记,最终标记,筛选回收
Java7默认的垃圾收集器(?默认启用吧)
G1垃圾收集器(-XX:+UseG1GC) G1(Garbage First):
垃圾收集器是在Java 7后才可以使用的特性,它的长远目标时代替CMS收集器
解析JDK 7的Garbage-First收集器
参考
G1(Garbage-first)
算法-复制算法&标记-整理
分待收集器
-XX 参数被称为不稳定参数
配置
JVM(HotSpot) 垃圾收集器
JVM(HotSpot) 垃圾收集器 - 简书
JVM 内存区域大小参数设置 - 简书
JVM垃圾回收算法
参考
回收器算法
回收器算法
JDK1.2 之前
引用计数器算法
JavaStack中的引用的对象。
方法区中静态引用指向的对象。
方法区中常量引用指向的对象。
Native方法中JNI引用的对象。
GC Root 引用点
GC Root
LocalVariableTable(局部变量表) -> stack(操作数帧) -> Constant pool(常量池)
线程中栈帧中 "运行时常量池引用"中会指向具体的对象在堆中的地址
虚拟机栈中引用的对象(本地变量表)
线程正使用
长期存在方法区中
方法区中静态属性引用的对象
长期存在方法区中
方法区中常量引用的对象
本地方法栈中引用的对象(Native对象)
全局生命
java中可作为GC Root的对象有
Untitled node
示意
根搜索算法
收集垃圾
Untitled node
标记-清除算法不需要进行对象的移动,并且仅对不存活的对象进行处理,在存活对象比较多的情况下极为高效
但由于标记-清除算法直接回收不存活的对象,因此会造成内存碎片!
存活对象比较多的情况
适合
标记-清除算法
Untitled node
将存活对象复制到一块新的,没有使用过的空间中,这种算法当控件存活的对象比较少时,极为高效
但是带来的成本是需要一块内存交换空间用于进行对象的移动
存活的对象比较少时
适合
复制算法
Untitled node
标记-整理算法采用标记-清除算法一样的方式进行对象的标记,但在清除时不同,在回收不存活的对象占用的空间后,会将所有的存活对象往左端空闲空间移动,并更新对应的指针
标记-整理算法是在标记-清除算法的基础上,又进行了对象的移动,因此成本更高,但是却解决了内存碎片的问题。
长期未收集的内存(碎片相对严重)
老生代
适合
标记-整理算法
回收算法
深入理解JVM--JVM垃圾回收机制
JVM垃圾回收算法
参考
类加载过程
类加载过程
所谓动态,就是在运行期间生成代理类
类加载与动态代理
共识
类声明周期分为:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、卸御(Unloading)
通过一个类的全限定名来获取定义此类的二进制字节流。
将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
哪部分运行时数据区支持此环节工作?
加载
验证:就是要对加载的二进制流文件进行各种检查,很好理解。
为类变量(static)分配内存并设置初始值,即所谓的"零值",但是不包括常量(final)。
public static int value=123; 初始后为 value=0;
对于static final类型,在准备阶段会被赋予正确的值
public static final value=123;初始化为 value=123;
如果是boolean值默认赋值为:false
如果是对象引用默认赋值为:null
载入永久代,运行时常量池的形成(常量池的信息的运行时表现形式)
static{} 被访问时,才会初始具体值
未初始化的属性值为0
方法区
准备
将常量池的符号引用替换成直接引用,这个阶段发生时间没有明确规定,但是有具体限制:在符号引用被使用之前,必须被解析
方法区:运行时常量池中符号引用向直接引用,解析
解析
连接阶段
1、访问类型的静态成员(final常量除外)和使用new关键字
2、反射调用
3、一个类型的父类型先初始化
4、包含main方法的主类
初始化的过程:编译器自动按顺序收集类变量赋值语句和静态语句块(static{})生成<clinit>()方法,如果一个类型没有类变量赋值以及静态语句块,就不会自动生成
特例:数组类由JVM自动生成,自动创建
new 创建对象操作
getstatic 访问类的静态变量操作
putstatic 给类的静态变量赋值操作
invokestatic 调用静态方法操作
总结(new对象,操作static目标)
初始化静态变量、静态块中的数据等(一个类加载器只会初始化一次)
类的new构造,并非等同初始化
简要
类型初始化
堆栈空间分配
局部变量表/操作数栈/动态链接/运行时常量池引用
栈帧
jvm栈
程序计算器(线程)
使用
GC回收
卸载
过程
启动(引导)类加载器(BootStrap ClassLoader)
-Djava.ext.dir
扩展类加载器(Extension ClassLoader)
-Djava.class.path
系统类加载器(System ClassLoader)
除启动类加载器外,每个加载器都有父的类加载器
如果一个类加载器在接到加载类的请求时,它首先不会自己尝试去加载这个类,而是把这个请求任务委托给父类加载器去完成,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。
好处:类复用
坏处:类重写困难
双亲委派模型
Tomcat自定了ClassLoader,并且也破坏了双亲委托机制,相同jar包独立不干扰
每次都new一个自定义的ClassLoader来加载新的Class文件。JSP的实现动态修改就是使用此特性实现
JSP的实现动态修改就是使用此特性实现
利用破坏双亲委派来java的类热部署实现
破坏双亲委派模型
加密实现思路:加载Class文件的方式灵活,我们可以自定义ClassLoader,把加密后的Class文件,在加载Class前先进行解密,然后在通过ClassLoader进行加载
双亲委派模型(自启动-扩展-系统-自定义)
使用用户自定义类加载器是为了便于扩展 Java 虚拟机的功能,支持动态加载并创建类。
自定义类加载器
JVM中的类加载器
JVM类加载机制 - 推酷
JVM 类加载机制深入浅出
参考