Java虚拟机内存划分与垃圾回收

    xiaoxiao2023-05-27  0

    Java虚拟机内存划分

    1.       程序计数器

    用于存储程序要执行的下一条指令,占用内存可忽略不计

    2.       虚拟机栈

    用于存储栈帧,每执行一个方法对应产生一个栈帧,用于存储局部变量,动态链接,操作栈,方法出口等

    3.       本地方法栈

    与虚拟机栈类似,区别在于虚拟机栈为Java方法服务,而本地方法栈为其它语言(本地方法)服务,例如安卓系统上的C/C++语言库。

    4.       堆

    产生具体实例对象的内存区域,也是GC的重点回收区,其分为新生代,老年代。新生代又Eden,from survivor , to suivivor区。

    5.方法区

    主要存储类文件信息(class文件存储的信息),比如魔数(用于标识是否能被虚拟机执行),JDK的主次版本号,常量池信息,访问标志(是否为Public,final等),当前类索引,父类索引,接口索引,字段表,方法表,属性表等(详见类文件结构)

    PS:常见的运行时常量区存在于方法区中,是方法区内存区域里的一部分。

     

    垃圾回收算法

    一般来说,确定一个对象是否要被回收,有两种方式,一是引用计数法,一个对象每被引用一次,则引用计数位加一,当引用数为0时,说明此对象处于可被回收的状态。二是根搜索算法,以一个对象为根节点(称为GCRoots),从这个节点开始向下搜索,搜索路径称为引用链,GC时,如果对象不在引用链上,则说明此对象可被回收。一般可作为GCRoots的对象有:虚拟机栈中引用的对象,本地方法栈中引用的对象,方法区静态属性引用的对象,方法区中常量引用的对象。JAVA采用的是后者。

    1.       新生代垃圾收集

    新产生的对象一般在Eden区(也是垃圾回收最频繁的区域·  ),大对象直接进入老年代,Eden区和 from survivor区用于产生新对象,当进行Minor GC(新生代垃圾回收,老年代垃圾收集称为FullGC)时,仍被引用的对象会被移到 to survivor区(垃圾回收中的复制算法),然后清空前两个新生代区的内存,to survivor区的对象经历若干次(称为(年龄,sun 的hotspot虚拟机默认为15岁即经历15次垃圾收集)收集后若还未死,则移到老年区。

     

    2.       老年代垃圾收集

    老年代的Full GC相对于Minor GC频率要少很多,一般采取的算法是标记-清除法,标记-整理法。标记-清除法,即GC时在可回收的对象上作一个标记,然后对做完标记的对象进行回收(直接清空老年区该对象所占的内存区域)。标记-整理法,因为清除法会造成内存碎片化,导致大对象无处存放,所以采用整理法,将所用对象的内存压缩在一起(移到一边)。另一边空出连续的内存空间用于存放大对象。

    PS:老年代之所以不采用复制算法,而采用标记-整理法,原因之一是因为空间分配担保机制,因为Eden,to survivor,from survivor的比例默认是8:1:1(hotspot虚拟机),to survivor作为备用区,并不直接产生新对象,有时复制过来的对象太多,备用区难以存放,这时有老年代作为担保,可以将一部分对象移到老年代,若老年代采取复制算法,则没有额外的空间作为担保,所以会直接清除一部分对象。

    3.       方法区垃圾收集

    方法区垃圾收集没有堆中那么频繁,应该说是很少GC,但不意味它不发生GC。方法区GC主要包括两部分,一是常量的回收,而是类信息的回收。当常量引用为0时,可以进行回收。而类信息回收要求就高的多,首先是没有存在该类产生的实例对象,其次是该类的类加载器(classLoader)被回收了,最后没有通过反射机制引用该类。才可以被回收,注意,是可以被回收,而不是一定会被回收。

    4.       虚拟机栈与本地方法栈

    这两个区域当相关方法(栈帧)执行完毕时,会自动释放内存。不需要额外的条件。

    PPS:hotspot虚拟机基于以上算法实现了许多收集器,如新生代的Serial(单线程回收)收集器,ParNew,Parallel Scanvenge(采用多线程回收),老年代的 Serial Old,Parallel Old,CMS等,新生代老年代通用的G1收集器(基于标记-整理算法),有兴趣的话可以查看此书《深入理解JAVA虚拟机》

     

    转载请注明原文地址: https://ju.6miu.com/read-1260752.html
    最新回复(0)