JVM图例
2022-03-29 14:35:59 46 举报
JVM图例是一种用于描述Java虚拟机(JVM)内存模型的图形表示方法。它展示了JVM内存中的各种组件,如堆、栈、方法区等,以及它们之间的关系和作用。通过JVM图例,开发者可以更直观地了解JVM内存的分配和管理方式,从而更好地优化代码性能和解决内存泄漏等问题。
作者其他创作
大纲/内容
...
Java虚拟机的解释执行引擎称为“基于栈的执行引擎”,其中所指的“栈”就是操作数栈
BootstrapClassLoader负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如rt.jar、charsets.jar等
Old
分配方式:指针碰撞:要求内存规整,指针进行偏移即可空闲列表:虚拟机就必须维护一个列表,记录上哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录否规整由采用的垃圾收集器是否压缩整理功能决定
Object5
验证:确保遵循语言规范,保证虚拟机安全
操作数栈OS,方法执行中字节码指令读写内容,如算数运算
GC Roots
Execution Engine 执行引擎
Java栈\\Stack8个基本类型、对象的引用、实例的方法栈容量只由-Xss参数设定如线程请求的栈深度大于虚拟机允许的最大深度,将抛出StackOverflowError,栈帧太大(本地变量)太多(太深)
本地方法库
对象所需内存的大小在类加载完成后便可完全确定
car
新生代1/3
生命周期伴随整个Java程序,垃圾回收发生区域
编译器优化1、公共子表达式消除2、数组范围检查消除3、方法内联4、逃逸分析
引用计数法:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1。任何时刻计数器为0的对象就是不可能再被使用。无法解决互相引用的问题(A引用B,B引用A)可达性分析算法:这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GCRoots到这个对象不可达)时,则证明此对象是不可用的。GC Roots的对象包括下面几种:虚拟机栈(栈帧中的本地变量表)中引用的对象,方法区中类静态属性引用的对象,方法区中常量引用的对象,本地方法栈中JNI引用的对象。不可达对象在一次分析后不会立即清除,先判定是否有必要执行finalize()方法,有必要则进入F-Queue,GC会堆F-Queue中对象进行二次标记,二次标记上的对象基本就被回收了。
占字节数,由于HotSpotVM自动内存管理系统要求对象起始地址是8字节倍数非必然存在
1.7:永久代PermGen-XX:PermSize非堆内存初始值,默认是物理内存的1/64-XX:MaxPermSize最大非堆内存,默认是物理内存的1/4-XX:MaxPermSize设置过小会导致java.lang.OutOfMemoryError: PermGen space--------------------------------------1.8:元空间Metaspace-逻辑区域,在本地内存-XX:MetaspaceSize ,初始,过小频繁Full GC-XX:MaxMetaspaceSize,过小 java.lang.OutOfMemoryError: Metaspace
这种方式的好处是,对象地址移动(GC)时,只需改变句柄池指针位置,栈中reference不用改变,缺点是多了一次寻址开销
程序计数器/PC寄存器/Program Counter Register每一个线程有一个计数器,线程私有一个指针,存储执行方法区中执行的字节码地址CPU轮转到后继续执行的位置地址
字面量:字符串,基本数据类型值,被声明为final的常量
Survivor1To
栈帧中的本地变量
链接阶段
字符串常量池StringTable:hash表string值可动态加入String.intern()
解析:将常量池内的符号引用替换为直接引用,解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行
静态变量
存活对象
堆/实例
逃逸分析分析对象动态作用域:对象是否能被外部方法或外部线程访问,如没有外部访问可进行栈上分配则如下优化:1、栈上分配:无需GC回收消耗,随着栈帧的出栈而销毁2、同步消除:线程同步本身是一个相对耗时的过程,如果无逃逸,对这个变量实施的同步措施也就可以消除掉3、标量替换:无逃逸并且对象可以被进一步分解时,虚拟机可能不创建该对象,而是将对象成员变量分解若干个被这个方法使用的成员变量所代替,这些代替的成员变量在栈帧或寄存器上分配空间,这样就不会因为没有一大块连续空间导致对象内存不够分配。开启标量替换参数(-XX:+EliminateAllocations),JDK7之后默认开启。
Object7
.classClass File
Thread n
=
new Car()
可回收对象
字符串常量池和静态变量,1.7前在方法区后移入堆
方法返回地址RA,方法被调用的位置
句柄池
Thread1
栈帧n
对象属性内容(包含父类继承的)相同宽度(占用字节数相同)的分配在一起(long/double,byte/boolean等)
对象类型指针
符号引用:类名、接口、方法、字段名、描述符等
Car
Java栈
Object4
Garbage Collection垃圾回收器FullGC回收时Stop-The-World
.java
Survivor0From
1、持有对象地址
方法区静态变量
LV、OS、DL、RA
栈Stack
对齐填充alignment/padding gap
1、遇到new、getstatic、putstatic或invokestatic这4条字节码指令时2、反射调用时3、初始化子类要先初始化父类4、虚拟机启动执行main方法的类5、使用JDK 1.7的动态语言支持时6、引用父类静态变量、引用常量、创建类数组时不会发生初始化
引用不可达
Object6
初始化:执行类构造器<clinit>()方法的过程(变量赋值,静态代码块)
堆
方法区常量
2、持有句柄地址
class类型指针例:0xf8005b814bytes
线程共享区
CustomClassLoader自定义类加载器:继承java.lang.ClassLoad,重写findClass方法
动态链接-DL,符号引用,指向动态常量池方法的引用,动态链接相对于解析过程中的符号引用转直接引用(静态链接)
线程终止,三者内存被释放,生命周期与所属线程相同
JOL工具可查看
文件格式验证:是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理,主要包括魔数、版本号、常量池等验证。基于二进制字节流进行的元数据验证:对字节码描述的信息进行语义分析,以保证符合java语言规范。字节码验证:对类的方法体进行校验分析,保证方法在运行时不会危害虚拟机的安全,主要目的是确定程序语义是合法的、符合逻辑的。符号引用验证:虚拟机将符号引用转化为直接引用的时候,对类自身以外的信息进行匹配性校验。主要目的是确保解析动作能正常执行。-Xverify:none参数来关闭大部分的类验证
运行时数据区RuntimeDataArea
存取控制器
常量池
方法区/元空间
安全管理器
安全软件包
--------------------------------------------------------------------------------------------------
标记-整理法:标记过程与标记清除法一致,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存,避免碎片。缺点是多了一次移动操作。标记-清除,标记-整理法 更适用于老年代
栈帧 Stack Frame
Interpreter解释器
方法区\\Method Area\\Non-Heap是概念区域1.7或之前用永久代实现,1.8元空间实现-转为本地内存类信息(模板)、方法信息、常量final、静态变量
Mark Word哈希码、GC分代年龄、锁状态、偏向线程ID等0x0000000000000001 (non-biasable; age: 0)8bytes
引用可达
老年代2/3
复制算法:幸存区from-to的复制交换,幸存区发生GC,复制from存活对象到to区,from区进行清空(避免内存碎片),此时身份互换,to区转换为from,from转换为to,默认当一个对象经历了15次(可配置)GC后还存活,则进入老年区。此算法缺点在于内存空间浪费(to是空的)。极端情况如对象100%存活,则出现反复拷贝效率较低,所以应用场景适用新生代,对象存活度低。注:如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。
常量池类加载时到运行时常量池
堆\\Heap-Xms:默认内存的1/64-Xmx:默认内存的1/4通常设置成一致,防止每次GC后波动空余堆内存小于40%时,JVM就会增大堆到-Xmx的最大限制;空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。如果-Xmx不指定或者指定偏小,可能会导致java.lang.OutOfMemory错误新生代和老年代;新生代再有Eden空间、From Survivor空间、To Survivor空间
Object3
ExtClassLoader负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR类包
AppClassLoader应用程序类加载器:加载ClassPath下的类包,就是加载自写类
双亲委派机制,先向上委托加载类,一直到Boot加载器,然后逐级向下判断是否可加载完成,可以则结束,不可以则抛出异常向子加载器加载。可以防止java类被篡改
内存分配时非线程安全,两个线程使用了同一指针位置进行分配,两种处理方式:1、同步,CAS+失败重试2、TLAB,内存分配的动作按照线程划分在不同的空间进行,即每个线程在堆中预先分配一小块内存:本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)线程要分配内存,就在TLAB上分,只有TLAB用完并分配新的TLAB时,才需同步锁定。虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB参数设定
方法区虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过,如果没有则类加载
沙箱安全机制
本地方法栈\\Native Method Stack专门的一块内存区域被声明为native的方法都会加入此调用本地方法接口
本地方法栈JNI引用对象
本地方法接口(JNI)\\Java Native Interface例如:new Thread().start();->private native void start0();扩展Java使用,融合其他语言,如调用早起的C、C++执行本地方法库的方法
将代码归入安全域
标记-清除法:基础算法,后续算法对它的改进。原理上是先进行标记(标记方式如上),标记后进行统一回收,标记清除后会产生大量不连续的内存碎片,空间碎片太多可能会导致程序分配较大对象内存空间时,无法找到足够的连续内存而不得不触发一次GC。效率较低,多次扫描,扫描标记,扫描清除。
局部变量表Local Variable Table对象引用、基本数据类型
全盘负责:当一个ClassLoder装载一个类时,除非显示的使用另外一个ClassLoder,该类所依赖及引用的类也由这个ClassLoder载入。
对象实例指针
运行时常量池
\b也有使用句柄指针访问的方式,将此指针单独维护,非在此对象内存中
GC一次相互交换From、To,谁空谁是To,GC的复制算法
GC算法、分代收集算法
栈-reference两种方式
链接阶段解析为直接引用
Object1
对象头 Object Header
实例数据 Instance Data
Eden 8/10
Object8
线程独占区
Object2
数组对象时多此区域存储数组长度因为虚拟机通过普通对象的元数据信息确定对象大小,但从数组的元数据中无法确定数组的大小4bytes
栈帧1
JIT CompilerJIT编译器
0 条评论
下一页