JVM部分.No1.垃圾回收机制

java虚拟机的基本结构

类加载子系统:把类的信息加载进来。从方法区读class信息。

方法区:类的信息存放在方法区中,除此之外还有常量,以及字面值,即,等号右边的值。

java堆:程序启动的时候会建立java堆,几乎所有的对象,实例都回存放在java堆里面。堆的空间是线程共享的。

直接内存:

垃圾回收系统:

java栈:局部变量,方法参数,返回值等。

本地方法栈:本地方法,即:不是用java编写的,但是需要在java程序里面进行通信和调用。这些方法里面的局部变量,方法,参数,调用,返回值都放在本地方法栈里面的。

PC寄存器:程序计数器的功能,操作栈的指针,以及计算变量的指针。

执行引擎:

1.Java堆

JVM部分.No1.垃圾回收机制

 以上图中eden:s0:s1=8:1:1,新生代和老年代相比大概是2:1。

     刚产生的对象是存放在eden区域的, 经过一段时间如果对象还存活的,就将此对象移到s1或者s0区域,那个区域为空就放到那个区域。如果经过多次(默认15次)垃圾回收还是存在,那么就放到老年代里面。eden基本上会回收掉80%的对象。

JVM部分.No1.垃圾回收机制

    通过new关键字在堆内存中实例化对象,java站存储变量名,并且存储着和堆内实例之间的映射关系,另外利用类名调用方法的映射也存储再java栈里面。

 jvm常见参数:

 -Xms:6000M,java初始堆的大小。

-Xmx:6000M,JVM最大堆的大小.

-XX:PrintGCDetail打印出垃圾回收的详细信息.

JVM三大性能调优参数:-Xms,-Xmx,-Xss.

     -Xms和-Xmx是对堆性能调优参数,一般设置的都一样,如果不一样,当Heap不够用,会发生内存抖动。一般都调大的这两个参数,并且两个大小一样。

     -Xss是对每一个线程栈的性能调优参数,影响堆栈调用的深度。

一、判断对象是否还存活

1.引用计数法

   给对象添加应用计数器,每当有一个地方引用它的时候,计数器就加1,当引用失效时,计数值就减1.当计数器为0的时候就被当作垃圾回收。

不足:没有解决对象之间相互循环引用的问题。即:类里面声明了一个Object对象,然后这个类实例化了两个对象,这两个对象分别把自己的实例化对象赋值到类里面的Object对象,这样就会产生对象之间循环引用问题。

2.可达性分析法

从一个根节点GC root开始,从这些根节点向下搜索,搜索所走过的路经称之为引用链,当没有引用链的时候就认为此对象不可用。而在java语言抓奶哥,可作为GC Roots的对象包括:

  1. 虚拟机栈(栈帧中的本地变量表)中引用的对象。
  2. 方法区中静态属性引用的对象。
  3. 方法区中常量引用的对象。
  4. 本地方法栈中JIN(即一般说的Native)应用的对象。

二、垃圾收集算法

1.标记清除算法

   先标记再清除。整个算法分为“标记”和“清除”两个阶段,首先标记出要回收的对象,标记完成之后统一回收所有标记对象。

   不足:效率问题,标记和清除两个过程的效率都不高。另外标记清除之后会产生大量的不连续内存随碎片,当之后的程序需要分配比较大的对象的时候没有足够的空间又会引发另一次垃圾收集动作。

2.复制算法

    如果新生代的内存不够可以直接放到老年代里面去,另外当某些对象实例存活次数达到了要求也会存放到老年代里面去的。。解决了标记清除的时候发生的内存碎片以及程序运行的时候产生内存抖动的问题。年老代里面没有没有空间进行复制。第一次垃圾回收的时候会把Eden里面存活下来的对象实例放到S0(from中),第二次垃圾回收的时候就会把S0和Eden里面的存活下来的对象实例放到S1中,因此一般情况下S0或则S1总有一个是空的。一般来说默认15次垃圾回收之后就会放在老年代中。

    不足:不适用年老代,因为年老代里面只有一个区域。

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

    解决复制算法无法解决老年代空间。分三步:标记、整理、清除。整理的过程:把年老代分为需要整理区域和不需要整理区域,把存活下来的对象实例全部放到不需要整理区域,把需要清除的区域放到需要清理的区域。

4.分代收集算法

    其实就是把前几种方法联合起来使用。如:新生代使用复制算法,年老代使用标记整理算法。而在新生代里面因为需要回收的垃圾比较多就需要更加频繁的垃圾回收,而在年老代则不需要这么频繁。

三、常见的垃圾收集器

    如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。

JVM部分.No1.垃圾回收机制

上图所示的垃圾收集器:上面部分适用于新生代,下面部分使用于老年代。两者之间如果有连线则说明可以两者可以搭配使用。

 

GC是什么时候触发的(常见面试题)

由于对象进行了分代处理,因此垃圾回收区域、实践也不一样。GC有两种类型:Scavenge GC和Full GC.

Scavenge GC:

      一般情况下,当新的对象生成,并且再Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区域进行,不会影响道年老代。因为大部分对象是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行.因而,一般在这里需要使用速度快、效率高的算法,使Eden区能尽快空闲出来。

Full GC:

     对整个堆进行整理,包括Yong、Tenured和Perm。Full GC因为需要对整个堆进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分功能工作就是对于Full GC的调节。有如下原因可能导致Full GC.

  1. 年老代(Tenured)被写满;
  2. 持久代(Perm)被写满;比较少
  3. System.gc()被显示调用;
  4. 上次GC之后Heapd各域分配策略动态;