Java JVM相关的灵魂拷问

^_^

JVM内存结构

开放式题目,具体可见前一章节

一般从两个维度出发:线程私有和线程共享。到每一个内存区域的细节点。

img

Java 虚拟机栈是基于线程的。哪怕你只有一个 main() 方法,也是以线程的方式运行的。在线程的生命周期中,参与计算的数据会频繁地入栈和出栈,栈的生命周期是和线程一样的。

栈里的每条数据,就是栈帧。在每个 Java 方法被调用的时候,都会创建一个栈帧,并入栈。一旦完成相应的调用,则出栈。所有的栈帧都出栈后,线程也就结束了。每个栈帧,都包含四个区域:

局部变量表

操作数栈

动态连接

返回地址

本地方法栈是和虚拟机栈非常相似的一个区域,它服务的对象是 native 方法。

程序计数器是一块较小的内存空间,它的作用可以看作是当前线程所执行的字节码的行号指示器。

堆是 JVM 上最大的内存区域,我们申请的几乎所有的对象,都是在这里存储的。我们常说的垃圾回收,操作的对象就是堆。

方法区,这个区域存储的内容,包括:类的信息、常量池、方法数据、方法代码就可以了。

什么情况下栈内存溢出?

java.lang.StackOverflowError 如果出现了可能会是无限递归。

OutOfMemoryError:不断建立线程,JVM申请栈内存,机器没有足够的内存。

描述new一个对象的流程!

具体见前一章节

Java对象会不会分配在栈中?

可以,如果这个对象不满足逃逸分析,那么虚拟机在特定的情况下会走栈上分配。

如果判断一个对象是否被回收,有哪些算法,实际虚拟机使用得最多的是什么?

引用计数法和根可达性分析两种,用得最多是根可达性分析。

GC收集算法有哪些?他们的特点是什么?

复制、标记清除、标记整理。复制速度快,但是要浪费空间,不会内存碎片。标记清除空间利用率高,但是有内存碎片。标记整理算法没有内存碎片,但是要移动对象,性能较低。三种算法各有所长,各有所短。

JVM中一次完整的GC流程是怎样的?对象如何晋级到老年代?

对象优先在新生代区中分配,若没有足够空间,Minor GC;
大对象(需要大量连续内存空间)直接进入老年态;长期存活的对象进入老年态。

如果对象在新生代出生并经过第一次MGC后仍然存活,年龄+1,若年龄超过一定限制(15),则被晋升到老年态。

Java中的几种引用关系,他们的区别是什么?

强引用

一般的Object obj = new Object() ,就属于强引用。在任何情况下,只有有强引用关联(与根可达)还在,垃圾回收器就永远不会回收掉被引用的对象。

软引用 SoftReference

一些有用但是并非必需,用软引用关联的对象,系统将要发生内存溢出(OuyOfMemory)之前,这些对象就会被回收(如果这次回收后还是没有足够的空间,才会抛出内存溢出)。

弱引用 WeakReference

一些有用(程度比软引用更低)但是并非必需,用弱引用关联的对象,只能生存到下一次垃圾回收之前,GC发生时,不管内存够不够,都会被回收。

虚引用 PhantomReference

幽灵引用,最弱(随时会被回收掉)

垃圾回收的时候收到一个通知,就是为了监控垃圾回收器是否正常工作。

final、finally、finalize的区别?

在java中,final可以用来修饰类,方法和变量(成员变量或局部变量)

当用final修饰类的时,表明该类不能被其他类所继承。当我们需要让一个类永远不被继承,此时就可以用final修饰,但要注意:

final类中所有的成员方法都会隐式的定义为final方法。

使用final方法的原因主要有两个:

  (1) 把方法锁定,以防止继承类对其进行更改。

  (2) 效率,在早期的java版本中,会将final方法转为内嵌调用。但若方法过于庞大,可能在性能上不会有多大提升。因此在最近版本中,不需要final方法进行这些优化了。

final成员变量表示常量,只能被赋值一次,赋值后其值不再改变。

finally****作为异常处理的一部分,它只能用在try/catch语句中,并且附带一个语句块,表示这段语句最终一定会被执行(不管有没有抛出异常),经常被用在需要释放资源的情况下

Object中的Finalize方法

即使通过可达性分析判断不可达的对象,也不是“非死不可”,它还会处于“缓刑”阶段,真正要宣告一个对象死亡,需要经过两次标记过程,一次是没有找到与GCRoots的引用链,它将被第一次标记。随后进行一次筛选(如果对象覆盖了finalize),我们可以在finalize中去拯救。

所以建议大家尽量不要使用finalize,因为这个方法太不可靠。在生产中你很难控制方法的执行或者对象的调用顺序,建议大家忘了finalize方法!因为在finalize方法能做的工作,java中有更好的,比如try-finally或者其他方式可以做得更好

String s = new String(“xxx”);创建了几个对象?

2个,

1、 在一开始字符串”xxx”会在加载类时,在常量池中创建一个字符串对象。

调用 new时 会在堆内存中创建一个 String 对象,String 对象中的 char 数组将会引用常量池中字符串。