#
JVM为什么能跨平台访问
java被称为一处编译,到处运行,那么它实现的机制到底是什么呢?关键就在jvm屏蔽了跨平台的多种处理。看图:
#
JVM组织构成
类装载子系统
负责将class文件加载到运行时数据区域。
运行时数据区域
我们代码里主要处理逻辑的地方
堆
-
首先堆是我们所有线程共享的区域。
-
存放我们new出来的对象,例如:new Student(); 或者数组对象。如:new int[]{};(注:有时候我们new出来的对象,例如student,并不一定会分配到堆上,可能经过逃逸分析,分配到栈上。关于逃逸分析,我们后面再讲)。
栈
这个和我们数据结构中的栈是一样的。规则:先进后出。
栈包括如下几部分:
-
局部变量
-
操作数栈
-
动态链接
-
方法出口
我们通过如下代码实例,依次来讲解各个含义。
package com.jvm;
public class JvmRunner {
public static final int month = 5;
public int show(){
int one = 10;
int two = 11;
int result = one+two ;
return result;
}
public static void main(String[] args) {
JvmRunner jvmRunner = new JvmRunner();
jvmRunner.show();
}
}
我们通过反编译该class文件,来看看具体如何操作的。鉴于反编译占用太大空间,我直接画图描述。
1、栈中为main线程分配空间默认1M,为main方法以及show方法分配栈帧空间(每个方法,都有独立空间)
2、我们重点通过show方法进行讲解
1)定义属性one
2)定义属性two
3)运算
本地方法栈
调用底层系统的方法,也是有变量以及运算的,这个也需要栈空间。
方法区(元空间)
方法区,jdk1.8后叫元空间。主要存放常量、静态变量以及类元信息的。是所有线程共享区域。
程序计数器
因为我们是多线程环境,所以jvm执行本线程到什么位置都是由字节码执行引擎负责记录的,方便由其它线程切换到本线程执行时,顺序执行上次停顿的位置。
字节码执行引擎
负责一行一行执行脚本的,同时也负责垃圾回收。
#
重点剖析堆
堆的组成结构:新生代(伊甸园区,存活区),老年代。
-
新生代:初次创建的小对象或未达到分代年龄阈值的对象均位于新生代
-
Eden: 伊甸园区,新创建的小对象一般先放入伊甸园区(注:大对象,与小对象是可以设置其大小的,超过这个值,直接会分配到老年代)
-
s1,s2:幸存区,假如内存不足时进行垃圾回收,会将Eden区存活的对象,放入幸存区
-
老年代:创建的大对象或达到分代年龄阈值的对象以及触发老年代动态年龄担保机制的对象
堆中对象创建流程
分配内存
当类加载完成后,就可以确定对象大小,此时就需要给对象“安置家”了,当然了java中存在并发情况,好比一群人看到了一片好地,先到先得。那么怎么保证每个人都有“地”呢?
划分内存的方法
-
指针碰撞:此方式对于内存规整的情况,犹如队列,依次向后追加,可以理解成做核酸排队的人,有胖的有瘦的(对象有大有小),但是都顺序指向一边。
-
空闲列表:此方式对于内存不规整的情况,可以理解成停车场。有些车位有车,有些没有,如果你开了一个大货车,需要占用2个停车位,就需要找停车位空着多的地方。
解决并发问题的方法
-
CAS:并发编程中最常用的方式之一,如果失败就重试。
-
本地线程分配缓冲:(Thread Local Allocation Buffer,TLAB)前面的方式是知道对象大小,此种方式是根据执行的线程,在堆中预先分配出一块空间供给该线程使用。通过-XX:+/-UseTLAB参数来控制是否启用TLAB,同时可搭配参数-XX:TLABSize指定TLAB大小【jdk7已经默认开启;jdk增加了是否启用的参数,大小一般为Eden的1%】,具体参数可查看地址:
https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
初始化
内存分配之后虚拟机就会将分配到的空间都初始化为默认值(不包括对象头),如果使用TLAB,这一工作过程也可以提前到TLAB分配时进行。这一步操作保证了对象的实例字段再java代码中可以不赋予初始值就能直接使用,程序能访问到这些字段的默认值。
设置对象头
对象头好比是一个身份证,表明了该对象是那个类的实例,怎样找到类的元数据信息,对象的哈希码,对象的分代年龄等。
示例展示对象头信息
1、代码中引入依赖jar包
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.16</version>
</dependency>
2、创建对象以及测试类
package com.jvm;
/**
* 描述 告白
*
* @author 阿牛
* @version 1.0
* @date 2022/06/05 15:13:48
*/
public class Confess {
int id;
String name;
byte islove;
Object info;
}
package com.jvm;
import org.openjdk.jol.info.ClassLayout;
/**
* 描述 计算对象大小
*
* @author 阿牛
* @version 1.0
* @date 2022/06/05 14:54:31
*/
public class ObjectMarkWord {
public static void main(String[] args) {
ClassLayout layout = ClassLayout.parseInstance(new Object());
System.out.println("Object对象输出...");
System.out.println(layout.toPrintable());
System.out.println("---------------\n");
ClassLayout intLayout = ClassLayout.parseInstance(new int[]{});
System.out.println("int[]对象输出...");
System.out.println(intLayout.toPrintable());
System.out.println("---------------\n");
ClassLayout