什么叫垃圾?垃圾是怎样产生的?垃圾怎么回收?
- java和C、C++中的垃圾区别
C和C++中的对象生存周期完全有编程者决定,而java中对象的生存周期默认由虚拟机决定,但是也允许编程者显式的调用gc()方法。因此对于java开发人员,我们可以肆意妄为的new对象,而不用管理对象的生存周期,这对于编程自然是方便的。不过,要想让程序更好的运行,我们就要理解java中的对象生存周期,从创造到被销毁的整个过程。下面就浅析一下java的对象生命周期。 - 谈引用(理解对象的存在)
在谈到对象的时候,就离不开引用(和c++中的指针很类似),在创建对象的时候,会在虚拟机栈中新建一块内存地址,此内存地址中存储的是对象的引用(reference类型)地址,指向java堆中或者方法区的常量池中的对象(这个对象是真实存在的,我们要进行操作的对象)。
在jdk1.2之后,java对引用进行扩充。分为以下四种:- ①强引用(Strong Reference):如“Object obj = new Object()”,这类引用是 Java 程序中最普遍的。只要强引用还存在,垃圾收集器就永远不会回收掉被引用的对象。
- ②软引用(Soft Reference):它用来描述一些可能还有用,但并非必须的对象。在系统内存不够用时,这类引用关联的对象将被垃圾收集器回收。JDK1.2 之后提供了 SoftReference 类来实现软引用。
- ③弱引用(Weak Reference):它也是用来描述非需对象的,但它的强度比软引用更弱些,被弱引用关联的对象只能生存岛下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在 JDK1.2 之后,提供了 WeakReference 类来实现弱引用。
- ④虚引用(Phantom Reference):最弱的一种引用关系,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的是希望能在这个对象被收集器回收时收到一个系统通知。JDK1.2 之后提供了 PhantomReference 类来实现虚引用。
注:四种引用的解释参考《深入理解Java虚拟机》,小伙伴们可以当做扩展,实际工作中很少会用到。
- 对象的创建后的状态以及发展
先贴一张图
参考上一篇《深入理解JVM之基本构架》- ①新对象当我们创建一般对象后,对象优先会存在【Eden】新生代中。
- ②大对象直接进入老年代。
大对象的含义:长字符串、数组等。在开发中应尽量避免使用大对象。因为大对象的存在会导致老年代空间提前触发【Full GC】,而【Full GC】的速度一般都会比较慢, 并且【Full GC】会导致Stop The World。 - ③长期存活的对象将进入老年代。
每一个对象都有一个对象年龄计数器。如果Eden中的对象经过一次【Minor GC】就会进入【Survivor】中,并且计数器变成1。每当发生一次【Minor GC】后,计数器就会加1,当到达一定的程度后,该对象就会晋升到【Old】老年代中。
注解:【Minor GC】和【Full GC】的区别
【Minor GC】(新生代GC):发生在【Eden Space】区域的垃圾回收,因为java中的对象存在的时间比较短,因此【Minor GC】是非常频繁的,而且速度也会很快。
【Full GC】(老年代GC)【Major GC】:前面三个名词属于一个意思,只是不同的叫法而已。指发生在【Old Space】区域的垃圾回收,【Full GC】的过程比较慢,大概是【Minor GC】的10倍以上,而且会伴随一次Stop-The-World。解释stop-the-World
在开始学习GC之前你应该知道一个词:stop-the-world。不管选择哪种GC算法,stop-the-world都是不可避免的。Stop-the-world意味着从应用中停下来并进入到GC执行过程中去。一旦Stop-the-world发生,除了GC所需的线程外,其他线程都将停止工作,中断了的线程直到GC任务结束才继续它们的任务。GC调优通常就是为了改善stop-the-world的时间。
- 分析对象怎么变成垃圾的? 什么时候垃圾可以回收?
java采用可达性分析算法,我们就不管它是什么算法,我们要知道的是什么时候对象变成垃圾了。
先贴图说话
在上图中,有一个GC ROOT Set区域,此区域中,下面的对象,无论通过多少个引用,只要能指向GC Roots的引用,此对象,就不会被回收。也就是从【GC Roots】到这个对象不可达时,此对象就变成了垃圾。而可以作为【GC Roots】的对象包括如下四种:- ①虚拟机栈(栈桢中的本地变量表)中的引用的对象
- ②方法区中的类静态属性引用的对象
- ③方法区中的常量引用的对象
- ④本地方法栈中JNI的引用的对象
但是要记住垃圾归垃圾,它并没有死亡,也没有被回收,只有发生【Minor GC】或者【Full GC】时才可能被回收。- 简介垃圾收集算法
- ①标记 - 清除算法(Mark-Sweep)
首先标记三种标记的内存状态,标记完成后,只是把【可回收内存】清除掉就可以了,这种算法就是简单的【标记-清除算法】。 - ②标记 - 整理算法(Mark-Compact)
首先标记三种标记的内存状态,这个步骤和标记-清除算法一样。标记完成后,把【存活对象】移向一端,然后直接清理掉边界以外的内存,这种算法就是【标记-整理算法】。 - ③复制算法
这种算法会浪费一半的内存空间,我们称为A、B两块空间。当使用A空间的时候,B空闲,每当A空间使用完后,就将存活对象复制到B空间中,并将A中的空间清理掉。这样每次都是对一半的内存进行内存回收。小伙伴们想一下,一半的空间都在浪费,这是无法承受的。因此,就出现了【分代收集算法】。
- ④分代收集算法
分代收集算法,就不贴图了,请看第一个图片。有【Eden】【S0】【S1】【Old】
因为在新生代中大多数对象都是“朝生夕死”的,因此不必要按照“1:1”的比例来分配空间,HotSpot虚拟机按照【Eden】:【S0】:【S1】=8:1:1的比例来配备内存,先说一下过程,首先使用【Eden】和【S0】,当回收时,将存活对象复制到【S1】中,并整理【Eden】和【S0】;以此往复。如果【S1】中的内存小于存活对象需要的内存的时候(【S1】空间不够用的时候),就借用【Old】的内存来担保。- 分析HotSpot的垃圾收集器(参考《深入理解Java虚拟机》P75)
- 分析HotSpot的垃圾收集器(参考《深入理解Java虚拟机》P75)
- ①Serial
- ②Serial Old
Serial收集器是一个单线程的收集器,但它的“单线程”的意义并不仅仅说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。Stop -The -World
Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用标记-整理算法。
-③ParNew
是Serial的多线程版本。和(CMS或者Serial Old配合工作。
- ④Parallel Scavenge
- ⑤Parallel Old
Parallel Scavenge收集器是一个新生代收集器,它也是使用复制算法的收集器,又是并行的多线程收集器。Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量(Throughput)。
Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。
-⑥CMS(Concurrent Mark Sweep)
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。目前很大一部分的Java应用集中在互联网站或者B/S系统的服务端上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验。CMS收集器就非常符合这类应用的需求。
-⑦G1(Garbage First)
G1(Garbage-First)是一款面向服务端应用的垃圾收集器。HotSpot开发团队赋予它的使命是未来可以替换掉JDK 1.5中发布的CMS收集器。与其他GC收集器相比,G1具备如下特点。并行与并发;分代收集;空间整合;可预测的停顿。
在G1之前的其他收集器进行收集的范围都是整个新生代或者老年代,而G1不再是这样。使用G1收集器时,Java堆的内存布局就与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分Region(不需要连续)的集合。- 参考文献
keycoding chenzhou123520 兰亭风雨《深入理解java虚拟机》周志明著《实战Java虚拟机》
如有转载,请注明出处。
- 参考文献
热门评论
不错
很好