CMS垃圾回收器 Concurent Marked Sweep
并行的标记垃圾回收器
获取最短停顿的回收器, 标记清除算法实现
缺点是:
1.对cpu资源敏感
2.无法处理浮动垃圾
3.有大量碎片产生
CMS 垃圾回收的六个步骤:
1. 初始标记
初始标记会触发 stop the world ,从垃圾回收的根对象开始查找,这个过程会暂停整个JVM,但是很快结束
2.并行标记
并发标记进行和用户线程同时执行。用户不会感觉卡顿
3.并发预清理
并发预清理阶段是并行的,标记新生代进入老年代的对象。
4.重新标记
扫描 堆中剩余的对象,然后重新从根对象进行扫描会 stop the word
5.并发清理
清理垃圾对象。和用户线程并发执行
6.并发重置
重置CMS数据结构,等待下一次垃圾回收
什么时候触发GC?
1. 执行 system.gc()的时候
2.老年代空间不足,一次Full GC 之后,然后不足 会触发 java.outofmemoryError:java heap space
3.永久代空间不足 永生代或者永久代java.outofMemory PerGen Space
4. minor之后 survior放不下,放入老年代,老年代也放不下,触发FullGC, 或者新生代有对象放入老年代,老年代放不下,触发FullGC
5.新生代晋升为老年代时候,老年代剩余空间低于新生代晋升为老年代的速率,会触发老年代回收
6. new 一个大对象,新生代放不下,直接到老年代,空间不够,触发FullGC
怎么避免频繁GC
1. 不要频繁的new 对象
2. 不要显示的调研system.gc()
3. 不要用String+ 使用StringBuilder
4.不要使用Long Integer 尽量使用基本类型
5.少用静态变量 不会回收
6.可以使用null 进行回收
什么是内存泄漏?如何避免?
内存泄露是,这个对象从Root可以找到,但是这个对象是无用的,永远不会被回收,这个就存在内存泄漏
Vector v=new Vector(10); for (int i=1;i<100; i++) { Object o=new Object(); v.add(o); o=null; }
为什么JDK1.8之后要移除Permgen Space 替换成了MetaSpace ?
permGen存放的数据内容:类的静态常量,字符串,类的元信息
MetaSpace存放的数据:和PermGen数据类型类似,描述类元数据 class已经被移除
区别是啥?
PermGen存放的是在JVM中的,如果load很多Class 的话,会导致OOM :PermGen error问题
但是MetaSpace是存放在 本地内存空间的,是依赖本地内存空间的大小。
为啥要替换成MetaSpace?
1. Class的大小无法控制,无法设定permGen 的大小,太小了,容易溢出,太大了,JVM内存浪费,也容易导致堆内存可用空间少,导致老年代溢出
2.字符串存放在永久代,容易导致内存溢出
3. 提高了FullGC性能,Metadata 到Metadatapointer之间不用扫描
会有个metaspace threshold, 可能存在的问题是,存在内存泄漏,不断的扩展metaspace 会导致机器内存不足, 所以也需要监控
什么是内存溢出?
程序申请内存的时候,内存没有空间分配了。
什么是守护线程?和非守护线程(用户线程)?区别是啥?
守护线程的作用是为用户线程提供服务便利的,垃圾回收器就是一个守护线程?
非守护线程线程全部结束,守护线程跟者结束。
测试用例:
import java.util.concurrent.TimeUnit; public class DaemonThreadTest { public static void main(String[] args) { Thread mainThread = new Thread(new Runnable(){ @Override public void run() { Thread childThread = new Thread(new ClildThread()); childThread.setDaemon(true); childThread.start(); System.out.println("I'm main thread..."); } }); mainThread.start(); } } class ClildThread implements Runnable { @Override public void run() { while(true) { System.out.println("I'm child thread.."); try { TimeUnit.MILLISECONDS.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }
将主线程停止工作,守护线程依然执行, 可通过如下设置
childThread.setDaemon(false);
守护线程是当非守护线程,全部结束,则程序结束
import java.util.concurrent.TimeUnit; public class DaemonThreadTest { public static void main(String[] args) { Thread mainThread = new Thread(new Runnable(){ @Override public void run() { Thread childThread = new Thread(new ClildThread()); childThread.setDaemon(true); childThread.start(); System.out.println("I'm main thread..."); } }); mainThread.start(); Thread otherThread = new Thread(new Runnable(){ @Override public void run() { while(true) { System.out.println("I'm other user thread..."); try { TimeUnit.MILLISECONDS.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }); otherThread.start(); } } class ClildThread implements Runnable { @Override public void run() { while(true) { System.out.println("I'm child thread.."); try { TimeUnit.MILLISECONDS.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
哪些内存需要回收 (GC)?
Eden 区域 MinorGc
Old Generation Major GC
FullGC 清理新生代 老年代,清理整个堆。
如何回收(GC)?
1. 引用计数法
增加一个引用 计数器+1
减少一个引用,计数器-1
计数器为0 表示不被引用
对象相互应用时 ,不能被回收
强引用
软应用 :非必需的对象,内存溢出前回收
弱引用 :非必需引用,强度比软引用更弱
虚引用 :最弱的一种引用
.可达性分析
根节点 GC Root开始,向下搜索,搜索过的路径为引用链, 当一个对象没有任何引用链和他相连,证明该对象不可用
有哪些回收算法?
1. 标记清除算法
标记需要回收的对象,然后统一回收
缺点:效率不高,会造成不连续的内存碎片
2.复制 清理算法,
内存分成两块,将存活的对象复制到另一块上。
缺点,内存使用效率不高
3. 标记压缩清理
标记需要回收的对象
让所有存活的对象向一端移动,压缩到一边去,还有边界外的是要回收的对象清理边界以外的内存
4.分代回收算法
根据对象存活的周期,将不同的内存划分成几块
不同存活周期采用不同的垃圾回收算法
有哪些垃圾回收器?
Serial 收集器
单线程收集器, 想回收的会暂停所有线程
ParNew
并行的 Serial的多线程版本 其他的和Serial 一样
CMS
上面已有介绍
G1
如何查看一个进程假死 ?
使用jstack 到处dump 文件