JVM 内存结构
一般从两个维度出发:线程私有和线程共享。
Java 虚拟机栈是基于线程的,在线程的生命周期中,参与计算的数据会频繁的入栈和出栈,栈的生命周期和线程是一样。
- 栈里的每条数据就是栈帧。在每个
Java方法被调用的时候,都会创建一个栈帧,并入栈。 一但完成相应的调用,则出栈。所有栈帧都出栈后,线程也就结束了。
每个栈帧,都包含四个区域:
- 局部变量表
- 操作数栈
- 动态连接
- 返回地址
- 本地方法栈是和虚拟机栈非常相似的一个区域,它服务的对象是
native方法。 - 程序计数器是一块较小的内存空间,它的作用可以看作是当前线程所执行的字节码的行号指示器。
- 堆是
jvm上最大的内存区域,我们申请的几乎所有对象,都是在堆上存储的。 垃圾回收操作的对象就是堆。 - 方法区, 这个区域存储的内容包括:类的信息、常量池、方法数据、方法代码。
什么情况下内存栈溢出?
java.lang.StackOverflowError 可能是无线递归导致的
OutOfMemoryError : 不断建立线程, JVM 申请栈内存机器没有足够的内存。
通过 new 创建 一个对象的流程
详情见 学习笔记-JVM-对象和垃圾回收机制 对象的分配 章节
Java 对象会不会分配在栈中
可以,如果对象不满足逃逸分析,那么虚拟机在特定情况下会走栈上分配。
如果判断一个对象是否被回收,有哪些算法,实际虚拟机使用得最多的是什么
引用计数法和可达性分析两种,大多数虚拟机用的是可达性分析。
详情见 学习笔记-JVM-对象和垃圾回收机制 判断对象的存活 章节
GC收集算法有哪些?他们的特点是什么?
复制、标记清除、标记整理。
复制速度快,但浪费空间,不会产生内存碎片。
标记清除空间利用率高,但有内存碎片。
标记整理算法没有内存碎片,但是要移动对象,性能较低。
详情见 学习笔记-JVM-对象和垃圾回收机制 垃圾回收算法 章节
JVM中一次完整的GC流程是怎样的?对象如何晋级到老年代?
对象优先在新生代区中分配,如果没有足够空间,则 Minor GC;
大对象(需要大量连续内存)直接进入老年代;长期存活的对象进入老年代;
如果对象在新生代中出生并经历过一次 Minor GC 后仍然存活,年龄 +1 ,若年龄超过一定限制(15),则被晋升到老年态。
详情见 学习笔记-JVM-对象和垃圾回收机制 对象的分配策略 章节
Java中的几种引用关系,它们的区别?
详情见 学习笔记-JVM-对象和垃圾回收机制 各种引用 章节
final、finally、finalize的区别?
final:
在 Java 中,final 可以用来修饰类,方法和变量(成员变量或局部变量)
当用 final 修饰类时,标明该类不能被其他类所继承。当我没需要一个类永远不能被继承是,就可以用 final 修饰,但要注意:
final 类中所有的成员方法都会隐式的定义为 final方法。
使用 final 方法的原因有两个:
- 把方法锁定,以防止继承类对其进行更改。
- 效率,早期的
Java版本中,会将final转为内嵌调用。但若方法过于庞大,可能在性能上不会有多大提升。因此在最近版本中,不需要final进行这些优化了。
final 成员变量表示常量,只能被赋值一次,赋值后其值不再改变。
finally:
finally 作为异常处理的一部分,它只能用在try catch语句中,并且附带一个语句块,表示这段语句最终一定会被执行(不管有没有抛出异常),经常被用在需要释放资源的情况下。
Object 中的 finalize() 方法
即使通过可达性分析判断不可达的对象,也不是非死不可。 真正宣告一个对象死亡,需要经过两次标记过程,一次是没有找到与 GCRoots 的引用链,它将被第一次标记。随后进行一次筛选(如果对象覆盖了 finalize()),我们可以在 finalize() 中去拯救。
String s = new String(“abc”);创建了几个对象?
2个
- 在一开始字符串
abc会在加载类时,在常量池中创建一个字符串对象。 - 调用
new时,会在堆内存中创建一个String对象,String对象中的char数组会引用常量池中的字符串。
详情见 学习笔记-JVM-对象和垃圾回收机制 String 对象是如何实现的 章节