码迷,mamicode.com
首页 > 编程语言 > 详细

[Java]理解JVM之四:垃圾回收机制

时间:2017-08-24 15:33:14      阅读:221      评论:0      收藏:0      [点我收藏+]

标签:释放   roo   理解   minor   强引用   引用计数器   判断   杀进程   基础   

JVM内存中的各个区域都会回收吗?

首先我们知道 Java 栈和本地方法栈在方法执行完成后对应的栈帧就立刻出栈销毁,两者的回收率可以认为是100%;Java 堆中的对象在没有被引用后,即使用完成后会被回收;方法区中的数据一般不会回收,只有在同时满足:所有实例被回收、加载该类的类加载器被回收、Class对象无法通过任何途径访问(包括反射)时才会回收;而程序计数器主要是记录指令执行的信息,在 HostSpot 虚拟机中是不会被回收的; 

对于堆中的垃圾回收机制如下:

一、何时触发对象的回收

  • 对象没有被引用时
  • 作用域发生未捕获的异常
  • 程序在作用域正常执行完毕
  • 程序执行了System.exit()
  • 程序被意外终止(被杀进程)

二、检测垃圾

1、引用计数法(JDK1.2之前)

当这个类被加载到内存以后,就会产生方法区,堆栈、程序计数器等一系列信息,当创建对象的时候,为这个对象在堆栈空间中分配对象,同时会产生一个引用计数器,同时引用计数器+1,当有新的引用的时候,引用计数器继续+1,而当其中一个引用销毁的时候,引用计数器-1,当引用计数器被减为零的时候,标志着这个对象已经没有引用了,可以回收了。但是如果有两个对象相互引用,则这种方式无法检测。

2、根搜索法

把所有的引用关系看作一张图,从一个 GC Root 节点开始搜索,搜索完成后没有被引用到的节点,即无用的节点,将其标记为垃圾。

可作为 GC Root 节点的对象有

  • Java 栈中引用的对象
  • 方法区中静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中引用的对象

对于引用在 Java 中存在四种引用类型

  • 强引用:直接 new 出来的对象是强引用,是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。 

  • 软引用:通过类 SoftReference 进行包装。当JVM中的内存不足的时候,垃圾回收器会释放那些只被软引用所指向的对象,如果内存还不足,才会抛出OutOfMemory错误。使用软引用对象时要判断是否还存活。软引用非常适合于创建缓存,当内存不足的时释放缓存。

  • 弱引用:通过类 WeakReference 进行包装。如果一个对象的所有引用都是弱引用,则这个对象会被回收。弱引用解决了对象在存活关系上的耦合关系。弱引用在集合中最常见,尤其是在哈希表中,哈希表的接口允许使用任何对象作为键,当一个键值被放入哈希表中后,哈希表就有了这些键和值对象的引用。如果是强引用,只要哈希表本身还存活则键和值对象不会被回收,弱引用就是为了解决这个问题。Java 中提供了 WeakHashMap 来满足这个需求。

  • 虚引用:通过类 PhantomReference 进行包装,主要用于检测对象是否已经清除。

 三、垃圾回收

在 JDK1.2 之前是通过引用计数法来标记垃圾,在之后是使用根搜索法来标记垃圾。当垃圾标记后有三种方式来回收垃圾:

  • 标记-清除
  • 复制
  • 标记-整理

1、标记-清除

标记-清除算法是当垃圾被标记后,直接回收其所占空间。这种方式不需要进行对象的移动,在存活对象较多的情况下效率很高,但是会造成内存碎片。

技术分享

2、复制

复制算法是在搜索垃圾过程中将存活对象复制到一块新的空间中,然后再清除垃圾。这种方式在存活对象比较少的情况下效率很高,但是需要一块内存空间用于对象的移动。

技术分享

3、标记-整理

标记-整理算法与标记-清除算法一样对对象进行清除,但是在标记-清除算法的基础上要将所有存活对象往同一区域移动,并更新对应的引用。这种方式成本较高,但是解决了内存碎片的问题。

技术分享

 

 

四、堆中分代的垃圾回收策略

因为不同对象的生命周期是不一样的,所以根据对象的生命周期不同可以采取不同的回收算法,以便提高回收效率。

技术分享

1、年轻代

在年轻代的内存中按照 8:1:1 的比例分为一个 Eden 区和两个 Survivor 区。大部分对象在 Eden 中生成,当 Eden 区满后触发 Minor GC(不一定等Eden区满了才触发),将 Eden 区和非空闲 Survivor 区存活对象复制到另一个空闲的 Survivor 区中。年轻代 minor GC 就是在两个 Survivor 区之间相互复制存活对象,直到一个 Survivor 区满后将存活对象复制到年老代中。

2、年老代

年老代存放对象的生命周期较长,因为对象在年轻代经历了多次回收后仍幸存。年老代与年轻代的内存比大概是1:2,当年老代也满了,将触发 Major GC 即 Full GC,对整个堆(包括新生代、旧生代和持久代)进行垃圾回收。

3、持久代

即方法区,存放的是常量、字节码文件信息,相对较为稳定,因为不会频繁创建对象。如果持久代满了后,触发Full GC。

 五、GC的类型

在年轻代的 GC 被称为 Minor GC,在年老代的 GC 被称为 Major GC,另外还有 Full GC 是指回收整个堆(包括新生代、旧生代和持久代)。

导致 Minor GC 的情况:

导致 Major GC 的情况:

导致 Full GC 的情况:老年代已满、持久代已满、调用 System.gc()、上次 GC 后堆的各域分配策略动态变化

上述的 Minor GC、Major GC 及 Full GC 我的理解是这仅仅是个称呼,来代表 GC 作用的区域。而关于 GC 的类型,有以下几种:

  • Serial GC:年轻代单线程收集器,使用复制算法,在 JDK1.3 之前广泛使用。当进行垃圾回收时,需要中断所有用户线程。

  • SerialOld GC:年老代单线程收集器,使用标记-整理算法,是 Serial GC 的老年代版本。

  • ParNew GC:年轻代多线程收集器,使用复制算法。可以理解为 Serial GC 的多线程版。在单 CPU 环境下效率远低于 Serial GC。

  • ParallelScavenge GC:并行收集器,使用复制算法,追求高吞吐量。

  • ParallelOld GC:并行收集器,使用复制算法,是 ParallelScavenge GC 年老代版本。

  • CMS GC(Concurrent Mark Sweep GC):高并发,占用 CPU 较高,使用标记-清理算法。

  • GarbageFirst GC(G1 GC)

[Java]理解JVM之四:垃圾回收机制

标签:释放   roo   理解   minor   强引用   引用计数器   判断   杀进程   基础   

原文地址:http://www.cnblogs.com/tengyunhao/p/7384336.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!