Java的内存区域
Java的内存区域主要由以下几个部分组成:
- 程序计数器(Program Counter Register):
- 每个线程私有的内存区域。
- 用于存储当前线程正在执行的指令的地址或返回地址。
- 当线程切换时,程序计数器用于恢复线程的执行。
- Java虚拟机栈(VM Stack):
- 线程私有的内存区域。
- 用于存储方法的调用和局部变量。
- 每个被调用的方法对应一个栈帧,包括局部变量、操作数栈和方法返回值等信息。
- 栈采用先进后出的方式,当栈内存满时,会抛出StackOverflowError异常;当动态扩展栈时,如果无法申请到足够的内存,将抛出OutOfMemoryError异常。
- 本地方法栈(Native Method Stack):
- 与Java虚拟机栈类似,但专门用于执行本地(Native)方法的调用。
- 在大部分Java虚拟机中,本地方法栈和Java虚拟机栈是合二为一的。
- Java堆(Heap):
- JVM管理内存中的最大的一块,所有线程共享。
- 几乎所有的对象实例和数组都会在这块分配内存。
- 由年轻代和老年代组成,其中年轻代又包括Eden区、Survivor区(From区和To区)。
- 当堆中没有内存分配给实例,也无法扩展时,会抛出OutOfMemoryError异常。
- 方法区(Method Area):
- 线程共享的内存区域。
- 用于存储已经被虚拟机加载的类信息(包括类的名称、方法信息、字段信息)、常量、静态变量、即时编译器编译后的代码等数据。
- 在JDK 8及之前的版本中,方法区位于永久代(PermGen)中;而在JDK 8及之后的版本中,永久代被元空间(Metaspace)取代。
Java的垃圾回收机制
Java的垃圾回收机制是JVM的自动内存管理机制,是一个运行在JVM后台的守护进程,由GC(Garbage Collection)实现。其主要目的是识别和回收不再被使用的对象,以避免内存泄漏和内存溢出。
- 判断对象是否存活:
- 通过对象的可达性算法(引用链法)分析来判断对象是否在被使用。如果一个对象不再被引用,那它就被认为是无用的,可以被回收的。
- GC Roots:
- 虚拟机栈中引用的对象。
- 方法区中的静态属性引用的对象。
- 方法区中常量引用的对象(即使用了static final)。
- 本地方法栈中(Native方法)引用的对象。
- 垃圾回收的过程:
- 标记阶段:从GC Roots开始,递归地搜索所有可达的对象,并将它们标记为存活。
- 清除阶段:遍历整个堆内存,将未标记的对象(即不可达的对象)进行回收。
- 垃圾回收器:
- Java提供了多种垃圾回收器,如Serial GC、Parallel GC、CMS GC、G1 GC等,它们各自有不同的特点和适用场景。
- 垃圾回收的触发时机:
- 垃圾回收的执行时间是不确定的,它可以根据内存的使用情况动态地调整其优先级。通常,当堆内存不足或满足某些特定条件时,会触发垃圾回收。
- finalize方法:
- 在垃圾回收机制回收任何对象之前,总会先自动调用该对象的finalize()方法(如果该方法存在)。但请注意,finalize方法并不是用来执行垃圾回收的,而是用于在对象被回收前执行一些清理工作。同时,从JDK 9开始,finalize方法已被标记为废弃。