在Java虚拟机中,对象和数组的内存都是在堆中分配的,垃圾收集器主要回收的内存就是再堆内存中。若是在Java程序运行过程当中,动态建立的对象或者数组没有及时获得回收,持续积累,最终堆内存就会被占满,致使OOM。算法
JVM提供了一种垃圾回收机制,简称GC机制。经过GC机制,可以在运行过程当中将堆中的垃圾对象不断回收,从而保证程序的正常运行。数组
咱们都知道,所谓“垃圾”对象,就是指咱们在程序的运行过程当中再也不有用的对象,即再也不存活的对象。那么怎么来判断堆中的对象是“垃圾”、再也不存活的对象呢?.net
每一个对象都有一个引用计数的属性,用来保存该对象被引用的次数。当引用次数为0时,就意味着该对象没有被引用了,也就不会在使用这个对象了,能够断定为垃圾对象。可是,这种方式有一个很大的Bug,就是没法解决对象间相互引用或者循环引用的问题:当两个对象相互引用,他们两个和其余任何对象也没有引用关系,它俩的引用次数都不为0,所以不会被回收,但实际上这两个对象已经再也不有用了。线程
为了不使用引用计数法带来的问题,Java采用了可达性分析法来判断垃圾对象。3d
这种方式能够将全部对象的引用关系想象成一棵树,从树的根节点GC Root遍历全部引用的对象,树的节点就为可达对象,其余没有处于节点的对象则为不可达对象。
日志
垃圾回收机制,无论采用是引用计数法,仍是可达性分析法,都与对象的引用有关,Java中存在四种引用状态:code
强引用 - 咱们使用的大部分引用实际上都是强引用,这是使用最广泛的引用。若是一个对象具备强引用,就表示它处于可达状态,垃圾回收器毫不会回收它,即使系统内存很是紧张,Java虚拟机宁愿抛出OutOfMemoryError
错误,使程序异常终止,也不会回收被强引用所引用的对象。所以,强引用是形成Java内存泄露的主要缘由之一。cdn
软引用 - 一个对象只具备软引用,若是内存空间足够,垃圾回收器就不会回收它,若是内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就能够被程序使用。对象
弱引用 - 一个对象只具备弱引用,那就相似因而无关紧要的。弱引用和软引用很像,但弱引用的引用级别更低。弱引用与软引用的区别在于:只具备弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程当中,一旦发现了只具备弱引用的对象,无论当前内存空间足够与否,都会回收它的内存。blog
虚引用 - 一个对象仅持有虚引用,那么它就和没有任何引用同样,在任什么时候候均可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收的活动,咱们日常通常不会使用。
经过可达性分析算法可以断定哪些对象是须要回收的了,那么回收具体须要怎样去执行呢?
首先须要标记能够回收的对象内存,而后在对回收的内存进行清除。
和标记-清除算法不一样的是,标记-整理算法在标记后不直接清理可回收内存,而是将存活对象都移动到一端,而后清除掉可回收内存。
复制算法须要先将内存分为两块,先在其中一块内存上分配内存,当这块内存被分配完后,则执行垃圾回收,而后把存活对象所有复制到另外一块内存上,第一块内存则所有清空。
在Java虚拟机中,对象的生命周期有长有短,大部分对象的生命周期很短,只有少部分的对象才会在内存中存留较长时间,所以能够依据对象生命周期的长短将它们放在不一样的区域。在采用分代收集算法的Java虚拟机堆中,通常分为三个区域,用来分别储存这三类对象:
新生代 - 刚建立的对象,在代码运行时通常都会持续不断地建立新的对象,这些新建立的对象有不少是局部变量,很快就会变成垃圾对象。这些对象被放在一块称为新生代的内存区域。新生代的特色是垃圾对象多,存活对象少。
老年代 - 一些对象很早被建立了,经历了屡次GC也没有被回收,而是一直存活下来。这些对象被放在一块称为老年代的区域。老年代的特色是存活对象多,垃圾对象少。
永久代 - 一些伴随虚拟机生命周期永久存在的对象,好比一些静态对象,常量等。这些对象被放在一块称为永久代的区域。永久代的特色是这些对象通常不须要垃圾回收,会在虚拟机运行过程当中一直存活。(在Java1.7以前,方法区中存储的是永久代对象,Java1.7方法区的永久代对象移到了堆中,而在Java1.8永久代已经从堆中移除了,这块内存给了元空间。)
分代收集算法也就根据新生代和老年代来进行垃圾回收的。
对于新生代区域,每次GC都会有不少垃圾对象被回收,只有少许存活。所以采用复制回收算法,GC时把剩余不多的存活对象复制过去便可。
在新生代区域中,并非按照1:1的比例来进行复制回收,而是按照8:1:1的比例分为了Eden、SurvivorA、SurvivorB三个区域。其中Eden意为伊甸园,形容有不少新生对象在里面建立;Survivor区则为幸存者,即经历GC后仍然存活下来的对象。
老年代区域对象通常存活周期较长,每次GC时,存活的对象比较多,所以采用标记-整理算法,GC时移动少许存活对象,不会产生内存碎片。
Java虚拟机会把每次触发GC的信息打印出来,能够根据日志来分析触发GC的缘由。
参考:
Java内存回收机制--Java引用的种类(强引用、弱引用、软引用、虚引用)
理解Java垃圾回收机制
Java 技术之垃圾回收机制