JAVA ( 二 ) jvm垃圾回收和回收算法

前沿

之前讲过jvm的内存,程序计算器,本地方法栈,java虚拟机栈,堆。从下图可以看出,

程序计算器,本地方法栈,java虚拟机栈都是线程隔离的,也就是这些区域是随线程而生,也随线程而灭,

所以不需要做过多的回收考虑,

而对于 Java 堆和方法区,我们只有在程序运行期间才能知道会创建哪些对象,这部分内存的分配和回收都是动态的,垃圾收集器所关注的正是这部分内存。

了解之前,我们先看几个常见的面试问题。

JAVA中的创建出来的对象进入到JVM的哪个区域?

堆大致又可以分为那些区域,分别大概占比多少?

垃圾回收GC分为那几种,分别什么时候触发?常说的OOM内存溢出又是什么?

垃圾回收的算法有那些?怎么设置调优JVM参数?

     首先,JVM的堆大体上分为新生代区和老年代区,堆的大小可以通过参数设置 –Xms、-Xmx,新生代和老年代的比例默认是1:2,新生代区又分为Eden、From Survivor(后面from代替)、To Survivor(后面to代替),比例是 8:1:1,为什么是8:1:1,这个是有道理的,在后面回收算法的时候讲。

    当我们 new 一个对象的时候,绝大部分的对象都会进入到新生代的Eden区,有些大对象会直接进入到老年代区,当对象越来越多的时候,Eden区的剩余空间达到一个值的时候,会触发Minor GC,会对新生代里的对象进行一次回收,那怎么判断那些对象是否是可以回收的?当然就是那些没有引用的对象咯!那又怎么判断哪些对象是没有被引入的了?

1,引用计数法。这种方法就是在对象头部有个couner的计数器,当对象被引用一次时,改counter加一,当对象引用失效时,改计数器就会减一,这样,当我们进行垃圾回收的时候,只要判断这个对象的count是否等于0时,就可以判断是否进行对象回收。

      引用计数算法的实现简单,判定效率也很高,在大部分情况下它都是一个不错的算法。但是主流的 Java 虚拟机里没有选用引用计数算法来管理内存,主要是因为它很难解决对象之间循环引用的问题。循环引用就是 A对象里有个对象引用B,B对象里有个对象引用A,这个导致他们的计数器将永远不为0.

2,可达性分析算法,所有和 GC Roots 直接或间接关联的对象都是有效对象,和 GC Roots 没有关联的对象就是无效对象。

      有哪些GC root勒?

       1,Java 虚拟机栈(栈帧中的本地变量表)中引用的对象 

       2,本地方法栈中引用的对象

       3,方法区中常量引用的对象

       4,方法区中类静态属性引用的对象

   GC Roots 并不包括堆中对象所引用的对象,这样就不会有循环引用的问题。

          通俗的说,就是当我们要回收对象的时候,向上一直去找对象的引用,一直向上找,找到它的第一个祖宗,如果它是上面的GC root中的一个,那么这个对象就可达,就不能回收掉,否则就回收掉。

那我们回收对象的时候又有那么算法勒?

        1,标记-清除法    标记就是遍历所有的GC Roots,可达对象做标记为可存活对象,

                                  清除就是遍历所有的对象,将未标记的对象回收,同时把上一步的那些做标记的对象的标记清除。

                 这种算法就是 效率都不是很高,就算清除后也会产生大量的内存碎片。

        2,复制算法(新生代)        就是分成两块一样大小的内存,每次分配内存时,只使用一块,当这块区域达到一份阈值时,就会触发动作Minor GC,将内存中的可活对象复制到第二块空置内存中,然后清除第一块内存。

                这样做的优点就是不会有内存碎片,缺点显而易见,会浪费内存空间。

          ok,上面我们说的新生代区又分为Eden、from、to,比例是 8:1:1就是用的这种垃圾回收,像我们在新生代区会经常的触发Minor GC,Eden内存达到设定值会触发,from区达到设定值也会触发,每次触发,Eden的可存活对象会进入from区,对象年龄加一,from区Minor GC的时候,也是一样,可存活对象复制到to区,对象年龄加一(ps,当新生代里对象年龄默认到15的时候,对象进入老年代),from区内存清除。这个时候 之前的to区就会变成存放那些复制对象的from区,之前的form区就变成的空置的to区,来了个对调。

        当新生代进行Minor GC,可存活的对象占新生代超过内存大小的10%,也就是form存放不下的时候,这个时候通过分配担保机制将超过的对象直接分配到老年代区域中

        3,标记-整理算法(老年代)

             标记:它的第一个阶段与标记/清除算法是一模一样的,均是遍历 GC Roots,然后将存活的对象标记。

             整理:移动所有存活的对象,且按照内存地址次序依次排列,然后将末端内存地址以后的内存全部回收。因此,第二阶段才称为整理阶段。

              这是一种老年代的垃圾收集算法。老年代的对象一般寿命比较长,因此每次垃圾回收会有大量对象存活,如果采用复制算法,每次需要复制大量存活的对象,效率很低

             最后当老年代的内存达到一定值的时候,就会触发一次full gc,每次full gc会伴随一次Minor GC,当我们full gc也回收不了对象,释放不了内存的时候,这个时候就会报OOM了。

堆的参数配置

-XX:+PrintGC     每次触发GC的时候打印相关日志

-XX:+UseSerialGC      串行回收

-XX:+PrintGCDetails  更详细的GC日志

-Xms              堆初始值

-Xmx              堆最大可用值

-Xmn              新生代堆最大可用值

-XX:SurvivorRatio     用来设置新生代中eden空间和from/to空间的比例

猜你喜欢

转载自blog.csdn.net/shrek11/article/details/107251470