网站Logo GESONG

JVM内存模型和GC

gesong
2
2025-11-03

JVM内存模型

线程共享,存放对象实例,GC的主要区域

方法区

线程共享,用来存储已被虚拟机加载的类信息、常量、静态变量、JIT编译后的代码等数据

程序计数器

线程私有,当前线程的行号计数器

虚拟机栈

线程私有,每个方法被执行的时候都会创建一个栈帧用于存储局部变量表、操作栈、动态链接(对象的内存地址)、方法出口(返回值的内存地址)

本地方法栈

本地方法栈和虚拟机栈的作用类似,区别是本地方法栈服务于native方法

垃圾回收

垃圾收集算法

标记清除

算法分为“标记”和“清除”两个阶段,首先标记出所有需要回收的对象(引用计数法或可达性分析),在标记完成后统一回收掉所有的被标记对象。它是最基础的收集算法,后续的收集算法都是基于这种思路并对其缺点进行改进而得到的。

它的主要缺点有两个:一个是效率问题,标记和清除过程的效率都不高;另一个是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致,当程序在需要分配较大对象时无法找到足够的连续内存而不得不提前出发另一次垃圾收集动作。

标记复制

标记复制算法在标记清除算法的基础上,针对内存碎片问题做了一下优化,此算法把内存分为大小相同的两块,每次再使用的时候只使用其中的一块。当一块内存用完的时候,把存活对象复制到另外一块中,然后清除当前这块中的所有对象,如此反复。

缺点:使用复制收集算法,解决了内存碎片话严重的问题,但是存在缺陷就是每次只使用一半的空间,空间利用率受到影响,同时对于存活周期长的对象,复制次数多。

复制收集算法在对象存活率较高时就要执行较多的复制操作,效率将会变低。所以老年代一般不能直接选用这种算法。

标记整理

也叫标记压缩

根据老年代的特点,有人提出了另外一种标记整理算法,标记过程仍然与标记清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉端边界以外的内存,缺点是时间稍慢

分代收集算法

GC分代基于一个假设:绝大部分对象的生命周期都非常短暂,存活时间短。

分代收集算法,把java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最合适的收集算法。老年代中因为对象存活率高,没有额外空间对它进行担保,就必须使用标记清除或标记整理算法

新生代,又分为Eden区和Survivor区,使用标记复制算法,所有new出来的对象,都先存入Eden区,较大的对象Eden区可能没有足够的空间,可能会直接放入老年代区。

Survivor区又可以分为工作区和等待区,当Eden区存满垃圾回收时,会将Eden区和Survivor区中的工作区进行标记,将不是垃圾的对象,复制到Survivor区的等待区,之后工作区和等待区角色互换。

当对象经过多次(15次)新生代垃圾回收依然存活,这个对象就会存入老年代。

如果老年代满了,触发FullGC,将老年代和新生代一次GC,使用标记整理算法。

动物装饰