高频面试题-JVM

高频面试题-JVM

(1) JVM运行时内存区域划分

  • Java程序具体执行的过程:
  • 运行时数据区包括哪几部分:

    JDK8: 取消了整个永久代区域,取而代之的是元空间。常量池就不存放在堆中了,而是存放在方法区里面,与堆栈是并列关系
  • 程序计数器:程序计数器(Program Counter Register),也有称作为PC寄存器。它保存的是程序当前执行的指令的地址(也可以说保存下一条指令的所在存储单元的地址),当CPU需要执行指令时,需要从程序计数器中得到当前需要执行的指令所在存储单元的地址,然后根据得到的地址获取到指令,在得到指令之后,程序计数器便自动加1或者根据转移指针得到下一条指令的地址,如此循环,直至执行完所有的指令。由于程序计数器中存储的数据所占空间的大小不会随程序的执行而发生改变,因此,对于程序计数器是不会发生内存溢出现象(OutOfMemory)的。
  • Java栈也称作虚拟机栈(Java Vitual Machine Stack),也就是我们常常所说的栈.Java栈中存放的是一个个的栈帧,每个栈帧对应一个被调用的方法,在栈帧中包括局部变量表(Local Variables)、操作数栈(Operand Stack)、指向当前方法所属的类的运行时常量池(运行时常量池的概念在方法区部分会谈到)的引用(Reference to runtime constant pool)、方法返回地址(Return Address)和一些额外的附加信息.当线程执行一个方法时,就会随之创建一个对应的栈帧,并将建立的栈帧压栈。当方法执行完毕之后,便会将栈帧出栈。
    • 局部变量表:局部变量(包括在方法中声明的非静态变量以及函数形参)
    • 操作数栈:不断执行语句的过程
    • 指向运行时常量池的引用:因为在方法执行的过程中有可能需要用到类中的常量,所以必须要有一个引用指向运行时常量。
    • 方法返回地址:当一个方法执行完毕之后,要返回之前调用它的地方,因此在栈帧中必须保存一个方法返回地址。
  • 本地方法栈:本地方法栈与Java栈的作用和原理非常相似。区别只不过是Java栈是为执行Java方法服务的,而本地方法栈则是为执行本地方法(Native Method)服务的。
  • 堆:Java中的堆是用来存储对象本身的以及数组(当然,数组引用是存放在Java栈中的)。另外,堆是被所有线程共享的,在JVM中只有一个堆。
  • 方法区:方法区在JVM中也是一个非常重要的区域,它与堆一样,是被线程共享的区域。在方法区中,存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等。
    在方法区中有一个非常重要的部分就是运行时常量池,它是每一个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来。当然并非Class文件常量池中的内容才能进入运行时常量池,在运行期间也可将新的常量放入运行时常量池中,比如String的intern方法

(7) 如何判断对象是否可以回收或存活(引用计数法与GC Root可达性分析法区别; )

  • 引用数法:引用计数法师垃圾收集的早期策略,在这中方法中,堆中每个对象都有一个引用计数,每当有一个地方引用他时,引用计数值就+1,当引用失效时,引用计数值就-1,任何时刻引用计数值为0的对象就是可以被回收,当一个对象被垃圾收集时,被它引用 的对象引用计数值就-1,所以在这种方法中一个对象被垃圾收集会导致后续其他对象的垃圾收集行动。

    • 缺点:不完全准确,当两个对象相互引用的时候就无法回收,导致内存泄漏。
  • 可达性分析算法:基本思路就是通过一系列名为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的,下图对象object5, object6, object7虽然有互相判断,但它们到GC Roots是不可达的,所以它们将会判定为是可回收对象。
    可作为GC Roots对象的包括如下几种:

    a.虚拟机栈(栈桢中的本地变量表)中的引用的对象
    b.方法区中的类静态属性引用的对象
    c.方法区中的常量引用的对象
    d.本地方法栈中JNI的引用的对象

(8) 常见的GC回收算法及其含义

  • 标记-清除算法:
    • 标记阶段:先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象;
    • 清除阶段:清除所有未被标记的对象。
    • 缺点:标记和清除过程效率不高,标记清除之后会产生大量不连续的内存碎片。
  • 复制算法:(新生代的GC)
    • 将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,然后清除正在使用的内存块中的所有对象。
    • 缺点:内存缩小为原来的一半。
  • 标记-整理算法:(老年代的GC)
    • 标记阶段:先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象
    • 整理阶段:将将所有的存活对象压缩到内存的一端;之后,清理边界外所有的空间
    • 缺点:在标记-清除的基础上还需进行对象的移动,成本相对较高,好处则是不会产生内存碎片。
  • 分代收集算法:
    • 存活率低:少量对象存活,适合复制算法:在新生代中,每次GC时都发现有大批对象死去,只有少量存活(新生代中98%的对象都是“朝生夕死”),那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成GC。
    • 存活率高:大量对象存活,适合用标记-清理/标记-整理:在老年代中,因为对象存活率高、没有额外空间对他进行分配担保,就必须使用“标记-清理”/“标记-整理”算法进行GC。

(28) 类加载的过程:加载、验证、准备、解析、初始化

(29) 什么情况下会触发类加载;
> 创建类的实例
> 访问类的静态变量(除常量【被final修辞的静态变量】原因:常量一种特殊的变量,因为编译器把他们当作值(value)而不是域(field)来对待。如果你的代码中用到了常变量(constant variable),编译器并不会生成字节码来从对象中载入域的值,而是直接把这个值插入到字节码中。这是一种很有用的优化,但是如果你需要改变final域的值那么每一块用到那个域的代码都需要重新编译。
> 访问类的静态方法
> 反射如(Class.forName(“my.xyz.Test”))
> 当初始化一个类时,发现其父类还未初始化,则先出发父类的初始化
> 虚拟机启动时,定义了main()方法的那个类先初始化

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值