『JVM』运行时数据区

运行时数据区有哪些?

一个运行时数据区包含多个线程,这些线程共享方法区和堆,每个线程包含了程序计数器、虚拟机栈和本地方法栈

程序计数器(Program Counter Register) 

JVM会给每个线程一个独立的程序计数器,计数器之间互不影响。

这个区域主要负责记录正在执行的Java方法的JVM指令地址,即当前线程执行的字节码的行号指示器。如果是在执行native方法,则是未指定值(undefined)

 

程序计数器为什么要设为线程私有?

CPU需要不停的切换各个线程,为了能够准确的记录各个线程正在执行的当前字节码指令地址。

如:一个CPU并发执行三个线程,CPU首先执行线程1,假设线程执行到第四行,CPU切换为线程2,程序计数器就会存储线程1下一个字节码指令地址,

  然后执行线程2,线程2执行到第六行,CPU切换为线程3,程序计数器会存储线程2下一个字节码指令地址,如果程序计数器是共用的,

  线程2的字节码指令地址就会覆盖线程1的,当CPU切换为线程1时,就会执行错误

 

堆(Heap)

这个区域是被所有线程共享的一块内存区域,用来存放对象实例,并为对象实例分配好内存。

所有的线程共享Java堆,在这里还可以划分为线程私有的缓存区(TLAB:Thread Local Allocation Buffer)

 

Java虚拟机规范中规定:所有对象实例以及数组都要在堆上分配,Java堆也是垃圾收集器管理的主要区域,也叫 “GC堆” 。    

              堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的

 

Java堆区在JVM启动时即被创建,其空间大小也就确定了。堆的内存空间可以设置大小

比如:-Xms500M 、-Xmx1000M。其中-X表示它是JVM运行参数,ms是memory start的简称、mx是memory max的简称

在通常情况下堆空间不断的扩容和压缩,势必会形成不必要的系统压力,所以在生产环境中,JVM的Xms和Xmx会设置成大小一样

 

 对象分配过程详解

 一个对象产生之后,首先尝试在栈上分配,如果分配不下,判断是否为大对象(有参数设置),如果是大对象进制分配到Old区,如果不是,首先分配到线程本地分配TLAB,会进入Eden区,经过一次GC后,进入S1区,在经过一次GC进去S2区,年龄满足,进入Old区

 

 

-XX:-DoEscapeAnalysis  关闭逃逸分析

-XX:-EliminateAllocations  关闭标量替换

-XX:-UseTLAB  关闭TLAB

 

 对象何时进入老年代

  超过XX:MaxTenuringThreshold 指定次数  (YGC)

  PS:默认15

  CMS:默认6

  G1:默认15

  

 

 

 

 请解释一下对象的创建过程?

  T t =new T()
  1:classloading
  2:
  3:
  4:申请对象内存
  5:成员变量赋默认值
  6:调用构造方法<init>

 

 对象在内存中的存储布局?

  普通对象:
  对象头 8个字节
  指针
  对象内容
  padding对齐

 

  数组对象:
  对象头 8个字节
  指针
  数组对象长度
  数组对象内容
  padding对齐

 

 对象定位?
  1:句柄池
  2:直接指针

 

 堆异常

 

方法区(Method Area)

 具体的实现方式:

  1:Perm Space(JDK1.8之前)

      字符创常量位于PermSpace

    实现是永久代的方式

    FGC不会清理

  2:Meta Space(JDK1.8之后)

    字符创常量位于堆

    实现是元空间,元空间直接使用了本地内存

    会触发FGC清理

 

 运行时常量池(Runtime Constant Pool)

   运行时常量池是方法区的一部分。常量池表(Constant Pool)是Class文件的一部分

  当创建类或接口的运行时常量池时,所需的内存超过了方法区所提供的内存最大值,则JVM会抛出OutOfMemoryError

 

 方法区异常

 

 

 

栈(JVM Stack)

每个线程都有私有的PC、JVM Stack、Native Method Area,共享堆和方法区。

每个JVM Stack中有多个栈帧,每一个栈帧代表一个方法,每个栈帧都私有局部变量表、操作栈、动态连接、返回地址

一个方法的执行就是进栈与出栈

 

 栈帧(Stack Frame)

  每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态连接、方法出口等信息

  每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程

 

 局部变量表(Local Variable Table)

  局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。

  局部变量变中存放Java8大基本类型,如果是String,存的是引用的地址,实例对象在堆中

 

 操作栈(Operand Stack)

  在方法执行过程中,根据字节码指令,往栈中写入数据和提取数据,即入栈(push)和出栈(pop)

  操作数栈主要保存计算过程的中间结果,同时作为计算过程中变量临时的存储空间

 

 动态连接(Dynamic Linking)

  每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用。

 

 返回地址(Return Address)

  存放调用该方法的PC计数器的值。

  一个方法的结束,有两种方式:

   1:正常执行完成,如遇到return字节码指令

   2:异常退出。没有在该方法内处理

 

 虚拟机栈的面试题

  1:举例栈溢出的情况

    递归调用,通过-Xss设置大小

  2:调整栈的内存大小,就会保证不出现溢出吗

    不能,如果出现无限次循环肯定会溢出,调整栈大小只是保证溢出的晚一些

  3:分配的栈内存越大越好吗?

    越大是会延长StackOverflowError发生的时间。

  4:方法中定义的局部变量是否为线程安全

    如果只是一个线程操作该数据,则是线程安全的  

    如果多个线程操作该数据,该数据是共享数据,则存在安全问题

 

 

 

 直接内存(Direct Memory)

常用指令

store

load

pop

mul

sub

invoke

  InvokeStatic  调用静态方法

  InvokeVirtual  非静态,自带多态

  InovkeSpecial  new对象调用

 

posted @ 2020-08-26 13:35  打工人  阅读(181)  评论(0编辑  收藏  举报