MST
星途 面试题库

面试题:Java内存区域划分与垃圾回收机制基础

请简述Java内存区域主要分为哪些部分,以及垃圾回收机制在这些区域中是如何工作的,特别是堆内存的垃圾回收策略。
36.2万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

Java内存区域主要部分

  1. 程序计数器(Program Counter Register)
    • 是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。
    • 每个线程都有独立的程序计数器,此区域是线程私有的。
  2. Java虚拟机栈(Java Virtual Machine Stacks)
    • 也是线程私有的,生命周期与线程相同。
    • 描述的是Java方法执行的内存模型:每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
    • 局部变量表存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,它不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或者其他与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)。
  3. 本地方法栈(Native Method Stack)
    • 与Java虚拟机栈作用类似,不过它是为虚拟机使用到的本地(Native)方法服务的。
    • 本地方法一般是用C或C++实现的,同样也是线程私有的。
  4. 堆(Heap)
    • 是Java虚拟机所管理的内存中最大的一块,被所有线程共享。
    • 几乎所有的对象实例以及数组都在这里分配内存。Java堆是垃圾收集器管理的主要区域,因此也被称作GC堆(Garbage Collected Heap) 。
    • 根据分代收集理论,Java堆还可以细分为:新生代(Young Generation)和老年代(Old Generation),新生代又分为:伊甸园区(Eden Space)和幸存者区(Survivor Space,其中又有From Survivor和To Survivor两块区域)。
  5. 方法区(Method Area)
    • 也是各个线程共享的内存区域,它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
    • 在JDK 8及之后,方法区被元空间(Meta Space)替代,元空间使用本地内存。

垃圾回收机制在各区域工作情况

  1. 程序计数器、Java虚拟机栈、本地方法栈
    • 这三个区域随着线程的创建而创建,随着线程的结束而销毁,它们的内存分配和回收都具有确定性,因此不需要垃圾回收机制来管理。
    • 标记阶段:垃圾回收器首先会从根对象(如栈中引用的对象、静态变量引用的对象等)开始,通过可达性分析算法来标记出所有可达的对象,剩下的就是不可达对象,这些不可达对象就是可以被回收的对象。
    • 清除阶段:标记完成后,垃圾回收器会回收被标记为不可达的对象所占用的内存空间。
    • 堆内存的垃圾回收策略
      • 新生代垃圾回收策略
        • 复制算法:新生代中的对象大多“朝生夕灭”,存活率较低。当伊甸园区满了触发Minor GC(新生代垃圾回收)时,会把伊甸园区和From Survivor中存活的对象复制到To Survivor区,然后清空伊甸园区和From Survivor区。下次GC时,To Survivor变为From Survivor,原来的From Survivor变为To Survivor,如此循环。当对象在Survivor区熬过一定次数(默认15次)的GC后,会被晋升到老年代。
      • 老年代垃圾回收策略
        • 标记 - 清除算法:老年代对象存活率高,复制成本大,所以老年代一般采用标记 - 清除算法。先标记出老年代中所有不可达对象,然后回收这些对象占用的内存空间,标记 - 清除算法会产生内存碎片。
        • 标记 - 整理算法:为了解决标记 - 清除算法产生的内存碎片问题,有些垃圾回收器(如CMS在Full GC时采用的并发标记 - 清除算法,G1采用的标记 - 整理算法)会采用标记 - 整理算法。该算法在标记完不可达对象后,会将存活对象向一端移动,然后直接清理掉端边界以外的内存,这样就不会产生内存碎片。
  2. 方法区
    • 方法区的垃圾回收主要回收两部分内容:废弃的常量和不再使用的类型。
    • 废弃常量:如字符串常量池中存在的字符串对象,如果没有任何地方引用该字符串,就可以被回收。
    • 不再使用的类型:判定条件比较复杂,需要满足该类型所有的实例都已经被回收,加载该类型的类加载器已经被回收,该类型对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类型的方法等条件,该类型才能被回收。