JVM相关知识点

1. Java中有哪些垃圾回收算法

  • 引用计数法
    • 工作原理:每个对象都有一个引用计数器,当引用指向该对象时计数加1,当引用被解除时,计数减1.当计数为0时,表示当前对象没有引用,可以被回收
    • 优点:实现简单,能处理堆中的所有对象
    • 缺点:无法处理循环引用的情况
  • 标记-清除算法
    • 工作原理:首先遍历堆中的对象,标记出所有存活的对象,清除未标记的对象
    • 优点:实现简单,能处理堆中的所有对象
    • 缺点:标记和清除的过程会产生内存碎片
  • 复制算法
    • 工作原理:将堆中的对象复制到另一个空闲的内存区域,清除原区域的对象
    • 优点:效率高,可以避免碎片产生
    • 缺点:需要额外的内存空间,复制过程会消耗时间
  • 标记-整理算法
    • 工作原理:结合标记-清除算法以及复制算法 先标记所有存活的对象,然后将所有存活的对象向一端移动,清除未标记的对象
    • 优点:减少内存碎片,提高内存的利用率
  • 分代收集
    • 工作原理:将对象分为不同的代,年轻代经常进行垃圾回收,老年代相对较少

2. JVM方法区是否会出现内存溢出

重点:

jdk1.7前,方法区被实现为永久代,永久代的内存空间是固定的,不能动态扩展.如果加载的类过多或者常量池的数据过多,超出了永久代的内存空间,就会出现OOM:PermGen space错误
jdk8开始,方法区改用元空间.元空间不再使用堆内存,而是使用本地内存.所以元空间大小默认没有限制,但是可以通过参数来设置最大大小.如果超出这个大小,会报OOM:Metaspace错误

永久代大小设置:-XX:PermSize 和 -XX:MaxPermSize
元空间大小设置:-XX:MetaspaceSize(初始大小) 和 -XX:MaxMetaspaceSize(最大大小)

3. 为什么Java8移除永久代引入元空间

这就是上面提到的,主要是为了解决PermGen固定大小,容易导致内存溢出,GC效率低的问题
元空间使用本地内存,具备更灵活的内存分配能力,提升了垃圾回收的内存管理的效率

4. JVM的内存区域是如何划分的

重点

Java虚拟机运行时,会将内存分为:方法区,堆,虚拟机栈,本地方法栈,程序计数器

  1. 方法区
    1. 存储类信息,常量,静态变量.
    2. 属于线程共享区域,所有线程共享一个方法区
    3. jdk1.7之前,在永久代中;jdk1.8开始,放在元空间内
    1. 用于存放所有线程共享的对象和数组,是垃圾回收的主要区域
  2. 虚拟机栈
    1. 每个线程创建一个栈,用来保存局部变量,操作数栈,动态链接,方法出口信息
    2. 局部变量表中存储的是基本数据类型(int,long)以及对象引用
    3. 栈是线程私有的,生命周期与线程相同
  3. 本地方法栈
    1. 跟虚拟机栈相似,本地方法栈是线程私有的,生命周期与线程相同
    2. 为本地方法服务
  4. 程序计数器
    1. 每个线程创建一个程序计数器,用来记录当前线程执行的字节码指令地址
    2. 是线程私有的,生命周期与线程相同

堆内存的进一步划分

  • Eden区: 新对象最初会被分配到Eden区,且Eden区较大,会进行频繁的垃圾回收
  • Survivor区: 当Eden区满了,会触发一次Minor GC.存活的对象会被移动到Survivor区.分为S0和S1.
  • 老年代: 当Survivor区满了,会触发一次Major GC.存活的对象会被移动到老年代.老年代的内存空间较大,不会进行频繁的垃圾回收

5. 为什么Java新生代被划分为了Eden区和Survivor区

重点

因为新生代区域对象“人员流动”很快,所以比较适合复制算法。按照复制算法,需要把内存一分为二,但是这样的话内存利用率就只有50%

于是我们定义了Eden区+Survivor区。且Eden区+任意一个Survivor区的内存空间可以超过内存空间的50%。

这样就可以提高内存利用率。默认的配比是8:1:1。

利用两个Survivor区来交替接收每次Minor GC后存活的对象,比如当前使用的是Eden+S0,gc时将存活的对象移动到S1,Eden区和S0区会被清空。

  • Eden区: 新对象最初会被分配到Eden区,且Eden区较大,会进行频繁的垃圾回收
  • Survivor区: 当Eden区满了,会触发一次Minor GC.存活的对象会被移动到Survivor区.分为S0和S1。

如果单个Survivor区放不下存活对象该怎么办?

老年代兜底
如果单个Survivor区放不下存活对象,那么这些对象会被直接移动到老年代.
如果老年代剩下的空间也存放不下这些对象

  • 如果是CMS垃圾回收器,会触发CMS回收。如果CMS回收不足以腾出足够空间,则会触发Full GC
  • 如果是G1垃圾回收器,会触发Mixed GC

6. 什么时候会变成6:2:2?

重点

通常是JVM检测到对象存活率较高的时候,目的是给Survivor区更多的空间,避免对象过早晋升到老年代。

如果对象存活率升高,在一次Minor GC后,大量对象还活着,Survivor区可能不够用,这样就可以让更多对象在新生代多活几轮,减少晋升到老年代的压力