JVM底层原理
2020-10-20 09:55:03 0 举报
AI智能生成
JVM底层原理
作者其他创作
大纲/内容
类加载机制
类加载过程
加载
代码中用到这个类的时候,就会去加载
验证
文件格式验证
是否以魔数0xCAFEBABE开头
主次版本号是否在当前虚拟机处理范围之内
常量池中的常量是否有不被支持的常量类型
指向常量的各种索引值是否指向不存在的常量或不符合类型的常量
CONSTANT_Utf8_info型的常量是否有不符合UTF8编码的数据
Class文件中各个部分及文件本身是否有被删除的或附加的其他信息
元数据验证
这个类是否有父类
这个类的父类是否继承了不允许被继承的类(被final修饰的类)
如果这个类不是抽象类,是否实现了其父类或接口中要求的实现的所有方法
类中的字段、方法是否与父类产生矛盾
字节码验证
通过数据流和控制流分析,确定程序语义是合法的、符号逻辑的
符号引用验证
符号引用中通过字符串描述的全限定名是否能找到对应类
在指定类中是否存在符号方法的字段描述以及简单名称所描述的方法和字段
符号引用中的类、字段、方法的访问性是否可被当成类访问,确保解析动作能正常执行,NoSuchFieldError,NoSuchMethodError,IllegalAccessError
准备
为类变量(static 修饰的变量)分配内存并设置类变量初始值的阶段通常情况下,初始值设置为数据类型的零值
特殊情况:被final修饰的类变量,初始值为指定的值public static final int value = 123;
解析
虚拟机将常量池内的符号引用替换为直接引用的过程
解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行
初始化
初始化阶段是执行类构造器()方法的过程
如果初始化一个类的时候,发现他的父类还没初始化,那么必须先初始化他的父类
使用
卸载
类加载器
启动类加载器
负责将存放在<JAVA_HOME>\\lib目录中的,或者被-Xbootclasspath参数所指定的路径,并且是虚拟机识别的类库加载到虚拟机内存中
扩展类加载器
负责加载<JAVA_HOME>\\lib\\ext目录中,或者被java.ext.dirs系统变量所指定的路径中的所有类库
应用程序类加载器
负责加载ClassPath 环境变量所指定的路径中的类
自定义类加载器
根据自己需求加载类
双亲委派机制
工作过程:一个类加载器收到了类加载的请求,先把这个请求委派给父类加载器去完成,每一个层次的加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈无法完成这个加载时,子加载器才会尝试自己去加载
避免多层级的加载器结构重复加载某些类
分代模型
新生代
对象存活时间短
经历过几次GC仍未被回收的对象,会进入老年代,默认15次
老年代
对象存活时间长
永久代
就是指方法区
为什么要区分新生代和老年代
1.大多数对象存活时间极短,少部分对象存活时间长2.对于不同存活时间周期的对象,使用不同的垃圾回收算法
垃圾回收
什么时候会触发垃圾回收
1、新生代Eden区和S1区中无法存放更多的对象,会执行YGC
2、老年代区域无法存放更多的对象,会执行FGC
哪些对象是不能被回收的
通过可达性分析法来判断对象是否可被回收,即有GC Root根节点
GC Root节点:1、Class - 由系统类加载器(system class loader)加载的对象2、Thread - 活着的线程3、Stack Local - Java方法的local变量或参数4、JNI Local - JNI方法的local变量或参数5、JNI Global - 全局JNI引用6、Monitor Used - 用于同步的监控对象
总结:只有要你的方法里对象被局部变量或静态变量引用了,就是不可回收的
新生代垃圾回收
新生代采用复制算法执行垃圾回收
将新生代分为Eden区,Survivor1区,Survivor2区,默认空间比例 E:S1:S2=8:1:1
Eden区和S1区满了,将存活对象移动到S2区,回收Eden和S1区;当Eden区和S2区满了,将存活对象移动到S1区,回收Eden和S2区,以此类推
躲过15次GC后进入老年代
对象从S1区移动到S2区,对象的年龄就会增长一岁,当年龄到达15岁时,即经历了15次YGC对象仍然存活,则把对象移动到老年代通过JVM配置-XX:MaxTenuringThreshold 设置进入老年代的年龄,默认值为15
对象动态年龄判断
当前存放在Survior区的对象占用了Survivor区50%的内存大小,那么此时大于等于这批对象年龄的对象直接进入老年代
大对象直接进入老年代
通过JVM配置 -XX:PretenureSizeThreshold 对象字节大小,比如 1048576 字节就是 1MB
Minor GC后对象太多无法放入Survivor区怎么办
YGC后,没有足够的Survivor区存放存活对象,那么对象直接进入老年代
老年代空间分配担保规则
在执行Minor GC之前,JVM会检查老年代可用的空间大小,是否大于新生代所有对象的大小防止极端情况,Minor GC后,所有的对象都存活了下来,就会都进入老年代,老年代空间如果不够呢?假设在Minor GC之前发现老年代可用空间 < 新生代全部对象大小,校验是否设置 -XX:-HandlePromotionFailure 参数没有设置参数,则老年代执行FGC有设置参数,看看老年代的内存大小是否大于之前每一次Minor GC后进入老年代对象的平均大小,如果不是,则老年代执行FGC如果设置了参数,且老年代的内存大于每一次Minor GC后进入老年代对象的平均大小,则尝试执行Minor GC,此时Minor GC有以下几种情况(1)Minor GC后,存活对象小于Survivor区的大小,存活对象进入Survivor区(2)Minor GC后,存活对象大于>Survivor区,小于老年代可用内存大小,存活对象进入老年代(3)Minor GC后,存活对象大于Survivor区,大于老年代可用内存大小,老年代执行FGC,要是FGC过后,剩余空间仍不够放存活对象,那么就会导致OOM内存溢出了
老年代垃圾回收
采用标记整理算法
1.先标记存活对象2.将存活对象移动到一边防止垃圾回收后,产生过多碎片
JVM底层原理
Java程序如何在JVM中运行
java文件编译成class文件
类加载器将class文件加载进JVM
JVM基于字节码执行引擎执行类
内存区域
程序计数器
记录当前执行的字节码指令的位置
线程私有
方法区
放从.class文件里加载进来的类
1.8改名为Metaspace,元数据空间
虚拟机栈
执行一个方法,就会创建对应的一个栈帧,方法执行的过程就是一个栈帧在虚拟机栈中入栈到出栈的过程
栈帧数据:局部变量、操作数栈、动态链接、方法出口等
堆区
存放创建的各种对象
本地方法栈
native方法
如何设置JVM内存大小
-Xms:Java堆内存大小
-Xmx:Java堆内存的最大大小
-Xmn:Java对内存中新生代大小,扣除新生代剩下的就是老年代的内存大小
-XX:PermSize:永久代大小
-XX:MaxPermSize:永久代最大大小
-Xss:每个线程的栈内存大小
垃圾回收器
ParNew
多线程,回收新生代
CMS
多线程,回收老年代
G1
多线程,统一收集新生代和老年代
Serial
单线程,回收新生代
Serial Old
单线程,回收老年代
0 条评论
回复 删除
下一页