JVM的杂七杂八

163 阅读5分钟

.class文件

八个字节为单位的二进制流,通过javac编译。

  • 模数 标志它是class文件。模数就是标识文件类型的,
  • 版本号 次版本号、主版本号标志jdk版本,使jdk兼容。
  • 常量池计数器 计算常量个数,有变量和符号引用(包名、权限、字段名称。。。动态调用点和动态常量)
    java字段名和方法名的长度限制:const_utf8_info 类型,65535,64kb的长度限制
    常量池计数器从1开始,0给没有指向的类,比如没有父类和没有名称的类。更确切一点如Object类和匿名类\
  • 访问标识 类方法的访问属性,如acc_public、acc_private
  • 类索引、父类索引、接口索引 指向一个constant class info(常量池入口类型),再指向const_utf8_info找到权限名java.lang.object。
    接口索引集合,前面还有个接口索引计数器,计算接口数量。依旧指向一个constant class info(常量池入口类型),再指向const_utf8_info找到权限名。 字段表,也有字段表计数器,其中有个constantValue属性,比如final static修饰string类型这个变量就是constant属性了,这样避免类还没加载的时候就能用了。
    方法表,方法表计数器,有属性表,方法的代码需要存储到方法表中属性表的Code属性中。code->指令->存到code中,比如操作数栈的最大深度、局部变量表的存放空间(按slot方式存储不能变了)、方法长度、属性count、exception table...\
  • 字段表集合
  • 方法表集合
  • 属性表集合

类加载子系统

JVM类加载整体流程:加载、验证、加载、加载、验证、验证、准备、初始化(解析不知道在哪一步)

  • 加载 静态加载,获得class文件字节流,进入链接,链接后验证
  • 验证 class文件的模数和版本号
  • 加载 转化到方法区,结构从静态存储文件转化为运行时数据结构,涉及到class文件中的常量池和运行时方法区的常量池的区别和联系。
  • 加载 基于第三步加载,要在java堆内存生成一个Object类作为各种类的入口
  • 验证 元数据验证
  • 验证 字节码验证、符号指针验证
  • 准备 零值转化
  • 解析 游离状态,符号引用转化为直接引用,但它肯定在初始化前完成。
  • 初始化

双亲委派模型

启动类加载器、扩展类加载器、应用类加载器(系统类加载器)。还有继承方法自定义类加载器。

  • 启动类加载器 java home下面的lib目录
  • 扩展类加载器 lib里的ext目录
  • 应用类加载器 项目目录
  • 自定义类加载器 自定义的

向上委托、向下委派

student类先去系统类再去扩展再去启动,你不是javahome的就再往下传。
好处:顺序加载,这样肯定先加载父类。还能确保安全性和唯一性。
比如我们作死自己写了个java.lang.string,这玩意儿可以加载但确定它必须由类加载器+名称指定!
instanceof,判断一个类属不属于另一个类,这时候用它判断,返回false。因为类加载器不一样。

数组加载

它其实在内存里,但当它是个student数组,它还是需要类加载器。
唯一数组是系统类加载器的类名空间上,基础数据类型会标记到启动类加载器的命名空间上

常量

验证时是零,初始化赋值

初始化

调用类构造器<client>方法,它是自动生成的。

什么时候会立即对类初始化

new一个类的对象,但new数组不会(这是newarray)
new、getstatic、putstatic、invokestatic四条字节码指令,这是jvm自己能识别的。

  • 当jvm执行new指令时会初始化类。即当程序创建一个类的实例对象。
  • 当jvm执行getstatic指令时会初始化类。即程序访问类的静态变量(不是静态常量,常量会被加载到运行时常量池)。
  • 当jvm执行putstatic指令时会初始化类。即程序给类的静态变量赋值。
  • 当jvm执行invokestatic指令时会初始化类。即程序调用类的静态方法。 还有反射、父类没有初始化先初始化父类、default修饰的方法

运行时数据区

线程共享:堆(对象)
线程私有:程序计数器(几乎不出现oom异常)本地方法区(类型信息,方法区入口,.class(转化为运行时数据区),代码缓存数据(code属性),jdk1.8(永久代->元空间(其实就是方法区,可以使用所有资源)))虚拟机栈

class常量池转化为运行时常量池

intern()方法,将需要放入的数据放入到方法区中,有动态性

对象

对象头(hashcode/gc年龄/锁标识状态/锁状态/偏向线程ID/偏向时间戳)、实例数据、对齐填充(8的倍数)

64位的jvm new object占了多少内存

16字节,对象头(Markword占8字节)默认使用了指针压缩(classpointer)它有个指向四个字节(不开启就是0),对象实例填充(0字节),Padding(四字节,0)

对象如何被线程定位

  • 句柄 句柄池,线程指向句柄池,句柄池reference对象。只需要改变句柄池到对象的连接。
  • 直接指针 直接指向,一次寻址,但对象移动时要修改指针,并且指针放在线程里。

虚拟机栈、本地方法栈

虚拟机栈运行在虚拟机里,本地方法栈运行在本地
栈帧(一个方法):方法局部变量表(code属性里,slot定位的),操作数栈,动态链接,返回地址
虚拟机栈就是调用方法(进栈入栈)
局部变量表:存放一个栈帧在虚拟机栈中方法的局部变量和参数
操作数栈(虚拟机栈深度max_stack):方法调用的最大深度
overstackflow(线程请求的栈深度大于了虚拟机栈的max_stack
正常退出,异常退出(throw exception,返回给调用方)