jvm 这两天的一个总结
首先随便找了一个jvm的结构图
类加载器子系统
它包含三个大的阶段
分别是 : 加载(loading) -> 链接(linking) -> 初始化(Initialization)
加载: 通过类的全限定名获取到这个类的二进制字节流 -> 将这个字节流转换成方法区的运行时数据结构
链接: 验证->准备->解析
首先是验证
验证包括验证该class字节流包含的信息符合当前虚拟机的要求 保证被加载类的正确性 不会去危害虚拟机自身安全
主要包括四种验证
文件格式验证(是否以0XCAFEBABE开头)
元数据验证(
1.这个类是否包含父类【java中除了Object 其他类都应该有父类】
2.这个的是否exetends了一个不被运行继承的类 比如final修饰的类
3.如果该类不是抽象类 是否实现了父类【接口】还未实现的方法
4.类中的字段或方法是否与父类产生矛盾 如覆盖了父类的final字段或方法 或者覆盖不符合规则)
准备
类变量(静态变量)分配内存并设置初始值【即0或NULL】
这里不包含final修饰的static 因为final在编译时就会自动分配 准备阶段会显示初始化
例:

这里不会为实例变量分配初始化
静态变量会分配在方法区中 而实例变量会后对象一起分配在堆中
初始化
初始化阶段就是执行类构造器方法(<clinit>)的过程
此方法不需要定义 是javac编译器自动收集类中的所有赋值动作和静态代码块中的语句合并而来
构造器方法中指令按语句在源文件中出现的顺序执行
若该类具有父类 jvm会保证子类执行前 父类的已经初始化完成
class Animal{ static int a = 0; static { a = 10; } } class Cat extends Animal{ static int b =0 ; static { b = a; } } //结果Cat.b为10
虚拟机必须保证一个类的<clinit>方法在多线程中同步加锁
虚拟机栈
本地方法栈每个线程独有一个 每调用一层方法 都会压入一层[栈帧] 可以把栈帧理解成一个方法
栈帧中有以下几个部分
1: 操作数栈 存放临时操作数
2: 临时变量表 存放临时变量 它和操作数栈一样 在编译后他们的大小就已经是确定了
3: 返回地址 它代表当前方法执行完return或者抛出异常后 需要执行哪一行代码
4: 动态链接 动态链接里面存放的是符号被转换后的物理地址((ps:符号是一个抽象名称,例如方法名,变量名等,都是符号))
本地方法栈
与虚拟机栈相似 不过存放的是Native方法
程序计数器
记录了当前线程执行到了哪一行代码 它没有OOM问题和垃圾回收,同时也是jvm中执行最快的部分
堆
在jdk7及以前 堆由以下几个部分组成:新生代(伊甸园[Eden]区,幸存者0区S0,幸存者1区S2) 老年代 永久代
而jdk8以后 移除了永久代 使用元空间[方法区] 解决了永久区的OOM问题
当Eden区满了以后 会进行一次YGC(Minor GC[只对新生代进行垃圾回收]) ,将存活下来的数据转移到S0/S1中(两个幸存者区只会有一个有数据,轮流使用),并对数据的[存活时间+1]
然后当存活时间超过了最大阈值(默认为15)时,该数据就会被移入老年代
程序只有在老年代满了之后 或者使用System.gc()提出gc建议后 才会执行full gc
【Full GC, 会对整个java堆进行垃圾回收
Major GC, 只对老年代区域垃圾回收
Minor GC 只对新生代区域垃圾回收
Fixed GC 对新生代区域和部分老年代区域垃圾回收】
gc通常会导致(Stop the world)问题 而full gc持续的时间会比Minor GC长很多 所以 程序要尽量避免生命周期较短的数据进入老年代区域

方法区
还没看完

浙公网安备 33010602011771号