只要五分钟!JVM超全的运行时数据区分析

49 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

计算机当前矛盾及设计

  • CPU和内存及磁盘的读写速率矛盾,引入高速缓冲区
  • 多核超线程工艺带来的一致性,使用硬件一致性协议解决
  • 主存共享,缓冲区核心独享的数据存储划分

常量池

分类及定义

种类存放内容备注
静态常量池1.静态常量池是类文件描述的一部分
2.有字面量和符号引用组成,类加载完成之后会加载到运行时常量池
字面量:文本、字符串、final修饰的内容
符号引用:类、接口、方法、字段的描述信息
运行时常量池静态常量池被加载到内存之后,就变成了运行时常量池
字符串常量池由于字符串是最常用的类型,为了性能,单独开辟了一个空间存放字符串1.6及之前存放永久代方法区
1.7之后,字符串常量池存放进堆

高频面试

  1. String str = "aa";最多创建多少个对象。

最多创建一个对象。"aa"是一个字面量,会首先通过.equals去字符串常量池查找,如果没有找到,则在堆中创建"aa"的对象,并把该对象的引用维护到字符串常量池中,再返回该引用;如果找到了该对象,直接返回引用。

  1. String str = new String("aa");最多创建多少个对象。

最多创建两个,new String(),在堆中创建一个String对象,并且如"aa"被认为字面量,最多产生1个对象

  1. intern()
String s1 = new String("aa");
String s2 = s1.intern();
System.out.println(s1);//aa
System.out.println(s2);//aa
System.out.println(s1.equals(s2));//true
System.out.println(s1==s2);//false

intern()返回字符串的规范格式,并且保证字符串是来自同一字符串常量池。如果字符串已经存在,则用equals确定对象,如果字符串不存在,则创建字符串对象,并且返回引用。

String s1 = "aa";
String s2 = s1.intern();
System.out.println(s1);//aa
System.out.println(s2);//aa
System.out.println(s1.equals(s2));//true
System.out.println(s1==s2);//true

常量池的内存布局

常量池的内存布局.png

  • 直接内存:越过内核直接访问的内存
  • 方法区:
    • 类比于计算机主存,线程共享,虚拟机启动时创建,跟进程绑定,存放类结构信息,会OOM
    • 方法区是JVM的逻辑概念划分,每个版本的具体实现不一致,在JDK 8中就是Metaspace,在JDK6或7中是Perm Space

运行时数据区划分

Run-Time Data Areas

JVM的运行时数据区.png

运行时数据区创建时机共享范围存储内容内存限制垃圾回收异常错误
方法区随虚拟机启动创建同进程绑定,线程共享类的结构信息
各种方法代码
即时编译之后的代码
可伸缩调节,有大小参数配置不可以被垃圾回收器管理OOM
随虚拟机启动创建同进程绑定,线程共享存储所有的类实例和已分配的数组可伸缩调节,有大小参数配置被垃圾回收器管理,永远不能显式回收OOM
Java虚拟机栈线程创建的时候创建同线程绑定,线程独享存储这栈帧 frames (§2.6).
虚拟机栈只push和pop,栈帧由堆分配
可伸缩调节,有大小参数配置被GC管理OOM
StackOverflowError
程序计数器线程创建的时候创建同线程绑定,线程独享线程同一时间只能执行一个方法
如果非native方法,则存储虚拟机指令地址
如果native方法则值未定义
存储returnAddress或者本地方法的指针
随着线程管理
本地方法栈不依赖本地方法的线程不创建,依赖的随线程创建而创建线程独享用来调用native方法可伸缩调节,有大小参数配置被GC管理OOM
StackOverflowError

栈帧

  • 程序计数器是栈帧的一个变量,一个栈帧是一个invoke,一个方法。
  • A frame is used to store data and partial results, as well as to perform dynamic linking, return values for methods, and dispatch exceptions.
  • 方法调用是创建,调用完成销毁
  • 栈帧独有一个本地变量表,操作数栈,指向当前类方法的运行时常量池的引用

JVM栈帧关系.png

栈帧内容作用备注
局部变量表方法中定义的局部变量以及方法的参数
变量不能直接使用,只能通过指令加载进操作数栈,作为操作数使用
类方法(静态方法)索引的位置实际的操作位置就是方法指令,成员方法0位置是this
操作数栈压栈和出栈的方式存储操作数
动态链接将符号方法引用转换成具体的方法引用
解析阶段也做过符号引用转直接引用,方法在解析阶段无法确定加载的层级,深度,所以只能在运行时解析,无法在静态解析的时候解析

栈、方法区、堆的互相指向

指向举例备注
\Longrightarrow栈帧中变量指向对象
方法中 Object o = new Object();
方法区\Longrightarrowprivate static Object obj=new Object();
\Longrightarrow方法区类的信息存储在方法区中,堆如果需要创建类的话,需要指向方法区