运行时数据区

运行时数据区内有哪些东西

1.8以前: 线程共享的有堆和方法区(永久代是其实现方式) 线程独立的有本地方法栈、虚拟方法栈和程序计数器

1.8以后 线程共享的有堆 线程独立的有本地方法栈、虚拟方法栈和程序计数器 方法区的实现方法变为元空间放在内存中

程序计数器有什么用?生命周期说一下

通过改变程序计数器可以依次读取执行指令,从而实现代码的流控制 另外,每个线程都有一个独立的程序计数器,互不影响。

随着线程的创建而创建,线程消失而消失

虚拟机栈有什么用?是如何运作的?

虚拟机栈内是栈帧。 每调用一次函数,对应的栈帧就会被压入虚拟机栈。里面保存了局部变量表,操作数栈、动态链接、方法出口等信息。

堆内是存什么的

对象实例和数组

所有的对象实例都分配在堆上吗

不是。jdk1.7开始有逃逸分析,如果对象引用没有被返回或者未被外面使用,那么对象可以直接分配在栈上

堆内存是如何分布的

年轻代、老年代和永久代(也就是方法区) 其中,年轻代的Eden survivor0 survivor1的比例是8:1:1 年轻代和老年代的比例是1:2

年轻代什么时候会变成老年代?

每经过一次垃圾回收,年龄+1(从Eden到s区,年龄初始化为1)。年龄到15就会被晋升到老年代。 有别的情况: 会把年轻代中的所有按年龄排序,如果某个年龄的大小超过了s区的一半时,就取这个年龄和15的最小值作为晋升年龄

方法区存储了哪些信息

加载的类的信息,常量,静态变量

为什么要将永久代替换为元空间

因为永久代设置空间大小是很难确定的。空间小容易FullGC和OOM,分配大了就会浪费 而元空间是用本地空间

说一说运行时常量池的变化

1.7以前,运行时常量吃包含字符串常量 1.7 字符串常量池被移到了堆中 1.8 运行时常量池还在方法区中,只不过变成了元空间,到内存中去了

讲一下java对象创建的过程

  1. 类加载检查 检查能否在常量池中定位到这个类的符号引用,检查是否被加载、验证、准备、解析和初始化

  2. 内存分配 为新生对象分配内存,可以使用 指针碰撞 或 空闲列表法,取决于堆是否规整

  3. 初始化零值 将分配到的内存空间初始化为0值

  4. 设置对象头 设置一些信息在对象头中,比如属于哪个类,年龄等

  5. 执行init方法 按照程序员的意愿进行初始化

虚拟机对对象初始化时,如何保证线程安全

采用两种方式:

  1. CAS+失败重试
  2. TLAB,每个线程私有的缓存空间,如果能存的下就存,存不下在放到堆中。TLAB也在堆中。

对象的内存分布是怎么样的

对象头、实例数据和对齐填充

虚拟机中对象是如何访问定位的

栈上的reference数据来操作堆上的具体对象 有两种方法:使用句柄和直接指针

  1. 句柄:reference指向句柄,句柄中存着到对象实例的地址和到对象类型的地址

  2. 直接指针:reference直接指向对象实例数据,然后实例数据有指针指向对象类型数据

哪些包装类在常量池中有缓存数据,缓存数值是多少

Byte[-128,127] Short[-128,127] Integer[-128,127] Long[-128,127] Character[0,127] Boolean直接返回True or False

Integer i1 =33; Integer i2 = 33; i1==i2的结果是什么

Integer i3 = new Integer(33); i3==i2的结果是什么

true,因为能缓存到常量池中

false,因为new Integer是创建对象

讲一下JVM中两种内存分配的方法

指针碰撞: 内存规整的情况下使用,用一个指针表明左边是用过的,右边是没用过的

空闲列表: 堆内存不规整的时候用,用列表记录哪些内存可用

注意: 在操作系统中,是用位图或者链表法。