垃圾回收机制:
垃圾回收(Garbage Collection,GC),顾名思义就是释放垃圾占用的地方,防止内存泄漏。有效的回收利用内存,对内存堆中已经死亡或者长时间没用的对象进行清除和回收。
垃圾回收时间:
1.会在cpu空闲的时候自动进行回收
2.在堆内存存储满了之后
3.主动调用System.gc()后尝试进行回收
补充:System.gc()用于调用垃圾收集器,在调用时,垃圾收集器将运行以回收未使用的内存空间。它将尝试释放被丢弃对象占用的内存。 然而System.gc()调用附带一个免责声明,无法保证对垃圾收集器的调用。所以System.gc()并不能说是完美主动进了垃圾回收。
对象回收执行:
1.对象没有引用
2.作用域发生未捕获异常
3.程序在作用域正常执行完毕
4.程序执行了System.exit()
5.程序发生意外终止(被杀进程等)
补充:我们最容易想到的就是当对象没有引用的时候会将这个对象标记为可回收对象,那么现在就有一个问题,是不是这个对象被赋值为空以后就一定被标记为可回收对象了呢?并不是这个对象被赋值为空之后就一定被标记为可回收,有可能会发生逃逸!
判断是否存活:
确定回收对象有两个算法:引用计数法与可达性分析法。
1.引用计数法
在JDK1.2之前,使用的是引用计数器算法系统会为对象添加一个计数器,当有新的引用时加1,引用失效时减1。
优点:引用计数收集器可以很快的执行,交织在程序运行中。对程序需要不被长时间打断的实时环境比较有利。
缺点:无法解决两个对象循环引用的问题。假设有A和B两个对象之间互相引用,也就是说A对象中的一个属性是B,B中的一个属性时A,这种情况下由于他们的相互引用,从而垃圾回收机制无法识别。
2.可达性分析法
这个算法的基本思想是通过一系列称为“GC Roots”的对象作为起始点,从这些节点向下搜索,搜索所走过的路径称为引用链,
当一个对象到GC Roots没有任何引用链(即GC Roots到对象不可达)时,则证明此对象是不可用的。可以作为GCRoots的对象包括下面几种:
1.虚拟机栈(栈帧中的局部变量区,也叫做局部变量表)中引用的对象。
2.方法区中的类静态属性引用的对象。
3.方法区中常量引用的对象。
4.本地方法栈中JNI(Native方法)引用的对象。
3、finalize方法
对于可达性分析算法而言,若要判断一个对象死亡,需要经历两次标记阶段。
第一次标记:对象在进行可达性分析后发现没有与GCRoots相连的引用链,则该对象被第一次标记并进行一次筛选,筛选条件为是否有必要执行该对象的finalize方法
若对象没有覆盖finalize方法或者该finalize方法是否已经被虚拟机执行过了,则均视作不必要执行该对象的finalize方法,即该对象将会被回收。
若对象覆盖了finalize方法并且该finalize方法并没有被执行过,这个对象会被放置在一个叫F-Queue的队列中,之后会由虚拟机自动建立的、优先级低的Finalizer线程去执行
第二次标记:对F-Queue中对象进行第二次标记
如果对象在finalize方法中拯救了自己,即关联上了GCRoots引用链,那么在第二次标记的时候该对象将从“即将回收”的集合中移除如果对象还是没有拯救自己,那就会被回收。
引用的分类:
1.强引用
代码中普遍存在的类似"Object obj = new Object()"这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。
2.软引用
描述有些还有用但并非必需的对象。在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围进行二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。Java中的类SoftReference表示软引用。
3.弱引用
描述非必需对象。被弱引用关联的对象只能生存到下一次垃圾回收之前,垃圾收集器工作之后,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。Java中的类WeakReference表示弱引用。
4.虚引用
这个引用存在的唯一目的就是在这个对象被收集器回收时收到一个系统通知,被虚引用关联的对象,和其生存时间完全没关系。Java中的类PhantomReference表示虚引用。
补充:ThreadLocal中有强引用和弱引用的应用,并且有内存泄漏风险。
垃圾收集算法:
1.标记-清除(Mark-Sweep)算法
首先标记出所有需要回收的对象,标记完成后统一回收所有被标记的对象。
不足:
从效率的角度讲,标记和清除两个过程的效率都不高;
从空间的角度讲,标记清除后会产生大量不连续的内存碎片。
(内存碎片太多可能会导致以后程序运行过程中在需要分配较大对象时,无法找到足够的连续内存而不得不提前触发一次垃圾收集动作)
2.复制(Copying)算法
将可用的内存分为两块,每次只用其中一块,当这一块内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已经使用过的内存空间一次性清理掉。
优点:这样每次只需要对整个半区进行内存回收,内存分配时也不需要考虑内存碎片等复杂情况,只需要移动指针,按照顺序分配即可。
缺点:对象存活率较高的场景下要进行大量的复制操作,效率很低。万一对象100%存活,那么需要有额外的空间进行分配担保。(因此不适合老年代)
HotSpot 虚拟机采用这种算法来回收新生代。
3.标记-整理(Mark-Compact)算法
过程与标记-清除算法一样,不过不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉边界以外的内存。
补充:大批对象死去、少量对象存活的(新生代),使用复制算法,复制成本低;对象存活率高、没有额外空间进行分配担保的(老年代),采用标记-清理算法或者标记-整理算法。
垃圾回收器:
如果说垃圾收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。下图展示了
7种作用于不同分代的收集器,其中用于回收新生代的收集器包括Serial、PraNew、Parallel
Scavenge,回收老年代的收集器包括Serial Old、Parallel Old、CMS,还有用于回收整个Java堆的G1收集器。不同收集器之间的连线表示它们可以搭配使用。
Serial收集器(复制算法): 新生代单线程收集器,标记和清理都是单线程,优点是简单高效;
ParNew收集器 (复制算法): 新生代收并行集器,实际上是Serial收集器的多线程版本,在多核
CPU环境下有着比Serial更好的表现;
Parallel Scavenge收集器 (复制算法): 新生代并行收集器,追求高吞吐量,高效利用 CPU。吞
吐量 = 用户线程时间/(用户线程时间+GC线程时间),高吞吐量可以高效率的利用CPU时间,尽
快完成程序的运算任务,适合后台应用等对交互相应要求不高的场景;
Serial Old收集器 (标记-整理算法): 老年代单线程收集器,Serial收集器的老年代版本;
Parallel Old收集器 (标记-整理算法): 老年代并行收集器,吞吐量优先,Parallel Scavenge收
集器的老年代版本;
CMS(Concurrent Mark Sweep)收集器(标记-清除算法): 老年代并行收集器,以获取最短回
收停顿时间为目标的收集器,具有高并发、低停顿的特点,追求最短GC回收停顿时间。
G1(Garbage First)收集器 (标记-整理算法): Java堆并行收集器,G1收集器是JDK1.7提供的一
个新收集器,G1收集器基于“标记-整理”算法实现,也就是说不会产生内存碎片。此外,G1收
集器不同于之前的收集器的一个重要特点是:G1回收的范围是整个Java堆(包括新生代,老年
代),而前六种收集器回收的范围仅限于新生代或老年代。
ZGC (Z Garbage Collector)是一款由Oracle公司研发的,以低延迟为首要目标的一款垃圾收集器。它是基于动态Region内存布局,(暂时)不设年龄分代,使用了读屏障、染色指针和内存多重映射等技术来实现可并发的标记-整理算法的收集器。在 JDK 11 新加入,还在实验阶段,主要特点是:回收TB级内存(最大4T),停顿时间不超过10ms。优点:低停顿,高吞吐量, ZGC 收集过程中额外耗费的内存小。缺点:浮动垃圾目前使用的非常少,真正普及还是需要写时间的。
新生代收集器:Serial、ParNew、Parallel Scavenge
老年代收集器:CMS、Serial Old、Parallel Old
整堆收集器:G1,ZGC
JVM调优参数:
1.堆栈内存相关
-Xms 设置初始堆的大小
-Xmx 设置最大堆的大小
-Xmn 设置年轻代大小,相当于同时配置-XX:NewSize和-XX:MaxNewSize为一样的值
-Xss 每个线程的堆栈大小
-XX:NewSize 设置年轻代大小(for 1.3/1.4)
-XX:MaxNewSize 年轻代最大值(for 1.3/1.4)
-XX:NewRatio 年轻代与年老代的比值(除去持久代)
-XX:SurvivorRatio Eden区与Survivor区的的比值
-XX:PretenureSizeThreshold 当创建的对象超过指定大小时,直接把对象分配在老年代。
-XX:MaxTenuringThreshold设定对象在Survivor复制的最大年龄阈值,超过阈值转移到老年代垃圾
2.收集器相关
-XX:+UseParallelGC:选择垃圾收集器为并行收集器。
-XX:ParallelGCThreads=20:配置并行收集器的线程数
-XX:+UseConcMarkSweepGC:设置年老代为并发收集。
-XX:CMSFullGCsBeforeCompaction=5 由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行5次GC以后对内存空间进行压缩、整理。
-XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片。
3.辅助信息相关
-XX:+PrintGCDetails 打印GC详细信息
-XX:+HeapDumpOnOutOfMemoryError让JVM在发生内存溢出的时候自动生成内存快照。
4.排查问题用
-XX:+DisableExplicitGC禁止系统System.gc(),