我爱编程

JVM垃圾回收策略

2018-04-17  本文已影响26人  suxm

java内存区域划分

Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的“高墙”,墙外面的人想进去,墙里面的人却想出来。



java虚拟机在执行java程序的过程中,会把它管理的内存划分为若干个不同的数据区域,如下图所示
图片.png

程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的信号指示器
每条线程都需要一个独立的程序指示器,是为了程序切换后能恢复到正确的位置。
此内存区域是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError的区域

程序计数器、虚拟机栈、本地方法栈3个区域随线程而生、随线程而灭
栈中的栈帧随着方法的进入和退出而有条不紊地执行着入栈和出栈操作。每一个栈桢中分配多少内存是在类结构已经确定下来就已知的,这几个区域的内存分配和回收多具备确定性,这几个区域不需要过多考虑回收的问题,方法结束或者线程结束时。内存就跟着自动回收了。

java堆和方法区,只有在程序运行期间才知道会创建哪些对象,内存的分配和回收都是动态的。

JVM在进行垃圾回收前,需要判断哪些对象是需要回收的

给对象添加一个引用计数器,被引用时计数器值+1,引用失效计数器值-1,当计数器值为0时对象不可能再被使用;

主流Java虚拟机未选用该算法管理内存(未解决对象之间相互循环引用的问题)

实现简单,判断效率高(应用:FlashPlayer,Python等)

将"GC Roots"对象作为起始节点,向下搜索,搜索走过的路径为引用链;当一个对象到GC Roots没有引用链时,则该对象是不可用的;

可作为"GC Roots"的对象:

1.方法区中静态属性引用的对象

2.方法区中常量引用的对象

3.虚拟机栈引用的对象 (栈帧中本地变量表)

4.本地方法栈中JNI引用的对象 (Native方法)

GC算法

<figure> image

</figure>

标记-清除算法采用从根集合进行扫描,对存活的对象进行标记,标记完毕后,再扫描整个空间中未被标记的对象进行直接回收,如上图。

标记-清除算法不需要进行对象的移动,并且仅对不存活的对象进行处理,在存活的对象比较多的情况下极为高效,但由于标记-清除算法直接回收不存活的对象,并没有对还存活的对象进行整理,因此会导致内存碎片。

image

复制算法将内存划分为两个区间,使用此算法时,所有动态分配的对象都只能分配在其中一个区间(活动区间),而另外一个区间(空间区间)则是空闲的。

复制算法采用从根集合扫描,将存活的对象复制到空闲区间,当扫描完毕活动区间后,会的将活动区间一次性全部回收。此时原本的空闲区间变成了活动区间。下次GC时候又会重复刚才的操作,以此循环。

复制算法在存活对象比较少的时候,极为高效,但是带来的成本是牺牲一半的内存空间用于进行对象的移动。所以复制算法的使用场景,必须是对象的存活率非常低才行,而且最重要的是,我们需要克服50%内存的浪费。

image

标记-整理算法采用 标记-清除 算法一样的方式进行对象的标记、清除,但在回收不存活的对象占用的空间后,会将所有存活的对象往左端空闲空间移动,并更新对应的指针。标记-整理 算法是在标记-清除 算法之上,又进行了对象的移动排序整理,因此成本更高,但却解决了内存碎片的问题。

JVM为了优化内存的回收,使用了分代回收的方式,对于新生代内存的回收(Minor GC)主要采用复制算法。而对于老年代的回收(Major GC),大多采用标记-整理算法

上一篇 下一篇

猜你喜欢

热点阅读