JVM Weekly-1

JVM可视化大创周报-1

Posted by Chase Gu on July 3, 2020

第一章:走近JAVA

Java技术体系

  • JDK:java程序设计语言、java虚拟机、java类库
  • JRE:java类库API中的Java SE API自己和java虚拟机
  • Java Card、Java ME、Java SE、Java EE

Java虚拟机

  • Sun Classic VM

    JDK1.0提供,纯解释执行

    PS:1.1提供了内部类、反射等技术

  • Exact VM

    • 准确式内存管理(Exact Memory Managerment):虚拟机可以直到内存中某个位置的数据具体是什么类型,这也是垃圾收集时准确判断堆上的数据是否还可能被使用的前提
    • 摒弃了以前Classic VM基于句柄(handler)的对象查找方式。(书P53介绍句柄方式)
  • HotSpot VM

    热点探测,Exact VM也有热点探测

  • IBM J9 VM

    更加模块化的OpenJ9比HotSpot更利于源码学习

第二章:Java内存区域与内存溢出异常

运行时数据区域

程序计数器

  • 线程私有

  • 如果正在执行的是Java方法:记录正在执行的虚拟机字节码指令的地址

    如果正在执行的是本地方法:值为空。

    此内存区域是唯一一个在《Java虚拟机规范》中没有规定任何OutOfMemoryError情况的区域

Java虚拟机栈

  • 线程私有。生命周期与线程相同
  • 存储
    • 局部变量表
    • 操作数栈
    • 动态连接
    • 方法出口
    • ……
  • 局部变量表
    • 编译期可知的Java虚拟机基本数据类型
    • 对象引用:Reference类型
    • returnAddress类型:执行一条字节码指令的地址
    • 局部变量表所需的局部空间是完全确定的,方法运行期间不会改变大小

本地方法栈

Java堆

  • 线程共享
  • 不一定所有的对象实例都分配在堆:逃逸分析技术
  • 分代与否、分代设计根据收集器的不同而不同
  • 《Java虚拟机规范》规定,Java堆可以处于物理上不连续的内存空间中,但在逻辑上它应该是被视为连续的。但对于大对象,如数组,多数虚拟机可能要求连续的内存空间
  • 大小通过-Xmx和-Xms设定

方法区

  • 线程共享
  • 存储已被VM加载的类型信息、常量、静态常量、即时编译器编译后的代码缓存等数据。其中字符串常量池、静态变量已经移出
  • JDK8采用在本地内存中实现的元空间来代替永久代

运行时常量池

  • 方法区的一部分
  • Class文件中有常量池表,用于存放编译器生成的各种字面量与符号引用,这部分内容在类加载后存放到方法去的运行时常量池中
  • 一般来说,除了保存Class文件中描述的符号引用外,还会把有符号引用翻译出来的直接引用也存储在运行时常量池中

直接内存

  • 并不是虚拟机运行时数据区的一部分

  • NIO

    避免了在Java对和Native堆中来回复制数据

HotSpot虚拟机对象

对象的创建

  • 分配内存
    • 分配方式
      • 指针碰撞
      • 空闲列表
    • 并发分配
      • 同步:CAS+失败重试
      • TLAB:本地线程分配缓冲
  • 初始化为零值
  • 设置对象头
  • 构造函数
  • 对象引用入栈

对象的内存布局

  • 对象头(Header)
  • 实例数据(Instance Data)
  • 对齐填充(Padding)

对象头

  • 存储

    • 用于存储对象自身的运行时数据:哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等

    • 类型指针:对象指向类型元数据的指针,判断是哪个类的实例(这种方式不是绝对的),(对象类型数据存储在方法区,指针是指向这个的)

      如果是Java数组,对象头中还有长度数据

  • 占用32比特或64比特

  • 称为“Mark Word”

实例数据

  • 父类继承来的也要记录

  • 顺序:longs/doubles, ints, shorts/chars, bytes/booleans, oops(Ordinary Object Pointers, OOPS)

    在满足这个顺序的前提条件下,父类中定义的变量会出现在子类之前

    如果HotSpot的+XX:CompactFields为true(默认为true),子类之中较窄的变量也允许插入父类变量的空隙之中,以节省空间

对齐填充

HotSpot要求对象起始地址必须是8字节整数倍

对象访问定位

  • 句柄访问
  • 直接指针访问:也就是上面说的,对象实例数据需要存储到对象类型数据的指针

OutOfMemoryError异常

  • Q:方法区中永久代实现与元空间实现的区别

第三章:垃圾收集器与内存分配策略

判断对象是否死亡

引用计数算法

可达性分析算法

  • 可作为GC Roots的对象
    • JVM栈中引用的对象
    • 静态属性引用的对象
    • 常量引用的对象
    • 本地方法栈JNI引用的对象
    • JVM内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象等,还有系统类加载器
    • 所有被同步锁持有的对象
    • 反映JVM内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等
  • 可以有其他对象临时加入,如对堆中某区域进行回收,这些对象可能被其他区域的对象所引用,此时也要加入这些对象

引用

强引用

  • 不回收,哪怕抛出溢出异常

软引用:使用SoftReference

  • 只被软引用关联的对象,在再不会收会导致溢出异常的时候才回收

弱引用:使用WeakReference

虚引用:使用PhantomReference

  • 对该对象没有任何影响,只是为了回收的时候收到一个系统通知

finalize()

  • 只能被执行一次,并且不一定能够执行完,优先级很低
  • 可以在finalize()中关联上引用从而自救
  • 没有重写、已经调用过一次,视为没有必要执行
  • 很废