菜鸟笔记
提升您的技术认知

java垃圾回收算法

常用的垃圾回收算法:

1.引用计数算法

给对象添加引用计算器,每多一个引用的地方就加一,引用失效时就减一,当计数器为0时,表明对象未被使用,垃圾回收器可以收集该对象

特点:实现简单,效率高,python、微软COM等都是使用引用计数器算法管理内存。

缺点:难以解决由于对象间的相互循环引用的问题。

2.跟搜索算法

通过判断某个对象能否通过节点和"gc root"相连,如果没有,说明该对象为不可达对象,垃圾回收器可以回收该对象的内存空间。

特点:c#和java就是采用根搜索算法进行内存管理。

在java中能作为GC Root的对象包括以下几种对象:

a.java虚拟机栈(栈帧中的本地变量表)中引用的对象。

b.方法区中的类的静态属性引用的对象

c.方法区中的常量引用对象。

d.本地方法栈那个JNI本地方法的引用对象。

java虚拟机常用垃圾收集算法

1.标记-清除算法

最基础的垃圾收集器算法,分为“标记”和“清除”两个阶段,先标记处所需要回收的对象,标记完成后,统一回收掉所有被标记的对象。

缺点:1)效率问题,标记和清除的效率不高。

           2)清除后会产生大量的不连续的内存碎片,可能会导致在程序需要为较大对象分配内存时无法找到足够连续的内存,不得不提前触发垃圾收集动作。

2.复制算法:将内存容量分成大小相等的两块,每次只使用其中一块,当一块用完时,将还存活的对象复制到另一块去,然后把之前使用满的那块空间一次性清理掉,如此反复。

优点:内存分配的时候不用考虑内存碎片问题,只移动堆顶指针,按顺序分配即可,简单高效。

缺点:内存空间浪费大,每次只能使用当前 能够使用 内存空间的一半;当对象存活率较高时,需要有大量的复制操作,效率低。

3.标记-整理

标记整理是在标记-清算上改进得来,前面说到标记-清算内存碎片的问题,在标记-整理中有解决。同样有标记阶段,标记出所有需要回收的对象,但是不会直接清理,而是将存活的对象向一端移动,在移动过程中清理掉可回收对象。

优点:解决了之前内存碎片的问题,特别是在存活率高的时候,效率远高于复制算法。

4.分代收集算法

根据内存对象的存活周期不同,将内存划分成几块,java虚拟机中一般将内存划分成新生代和老年代,当新建对象时一般在新生代中分配内存,在新生代垃圾收集器回收几次后仍然存活的对象,将被移动到老年代,或者当大的对象在新生代中无法分配到足够连续的内存空间时也会直接分配到老年代。

jvm垃圾收集器的内存结构

由图可知堆内存被分为新生代和老年代,整个堆内存采用分带复制垃圾收集算法。

1.新生代

采用复制标记-清理算法收集垃圾,在新生代中存在大量短生命周期的对象,所以不需要讲新生代容量等量化分,而是将新生代划分为Eden、survivor from、survivor to 三部分,其中新生代内存容量的默认比例如上图所示是8:1:1。survivor from和survivor to区域中总有一个是空白的,只有Eden和其中一个survior也就是总容量的90%会被用来为新对象的撞见分配内存。这样内存浪费就少了。当新生代的内存空间分配不足时,仍然存活的对象会被分配到空白的survior内存区域中。Eden和非空白的survivor会被标记回收,两个survivor交换使用。

jvm对新生代的垃圾回收称为Minor GC,次数频繁,每次回收时间也短。

-Xmn 设置新生代内存大小。

2.老年代

老年代存活率一般比较高,所以采用标记-整理算法进行垃圾收集效率会比较高。

jvm对老年代垃圾回收称为MajorGC/Full GC,次数相对比较少,每次回收的时间也比较长。

当新生代中无足够空间为对象分配内存,老年代内存也无法回收到足够的空间时,堆会产生OOM异常。

Full GC触发条件:

1.老年代被写满

2.持久代被写满

3.System.gc()被显示调用

4.上一次GC之后Heap的各个域分配策略动态变化。

GC垃圾收集

Serial收集器(复制算法)

 新生代单线程收集器,标记和清理都是单线程,优点是简单高效。

Serial Old收集器(标记-整理算法)

老年代单线程单线程收集器,serial收集器老年代版本。

ParNew收集器(停止-复制算法)

新生代收集器,可以认为是Serial收集器的多线程版本,在多核cpu下有着比Serial更好的表现。

Parallel scavenge收集器(停止-复制算法)

并行收集器,最求高吞吐量,高效利用CPU。吞吐量一般为99%,吞吐量=用户线程时间/(用户线程时间+GC时间)。适合后台应用对交互响应要求不高的场景。

Parallel Old收集器(停止-复制算法)

老年代的Parallel scavenge的收集器,并行收集,吞吐优先

Concurrent Mark Sweep 收集器(标记-清理算法)

高并发、低停顿,追求最短GC回收停顿时间,cpu占用比较高响应时间快,停顿短,多核cpu最求高响应时间的选择。

GC内存泄漏问题

1.静态集合HashMap、Vector等的使用最容易出现内存泄漏,这些静态变量的生命周期和应用程序一致,所有的对象都不能被释放。

2.各种数据连接、网络连接、io连接等没有显示的调用close关闭,不能被GC回收导致内存泄漏。

3.监听器的使用,在释放的对象没有删除相应的监听器也可能导致内存泄漏。