JVM-垃圾回收

带着三个问题看垃圾回收 
1.回收谁
2.何时回收
3.怎么回收java

1.回收谁

引用计数法:给对象中添加一个引用计数器,每当有对象引用它时,计数器就加1,引用失效就减1,计数器到0的时候表明不能使用该对象,不能解决循环引用的问题算法

可达性分析:经过GCRoots作为起点,从这个起点向下搜索,当一个对象到GCRoots没有任何引用链相连的时候,这个对象是不可用的,能够回收。虚拟机栈(栈帧变量表中引用的对象),方法区(类的静态属性引用的对象,常量引用的对象),本地方法中JNI(Native引用的对象)
image.png数组

"食之无味,弃之惋惜"
强引用(承认OOM,也不会回收)
软引用(系统OOM以前,这些对象被回收)
弱引用(不管内存够不够,都会回收)
虚引用(只会收到回收通知)安全

"最后一刻挣扎"
一个对象的死亡,至少要被标记两次,第一次看有没有必要执行该对象中的finalize方法,若是该方法被调用过或者对象没有覆盖整个方法,就没有必要执行finalize。若是执行了finalize,能够在方法里面自救,自救方案是与引用链上任何一个对象关联便可。不建议使用服务器

方法区的回收
回收效率低,回收严谨。只有知足如下三点才会回收
1.该类的全部实例都被回收,堆中不存在任何该类的实例
2.加载该类的ClassLoader已被回收
3.该类的java.lang.Class对象没有任何地方被应用,没法经过反射来访问该类多线程

2.何时回收

应用线程空闲时
内存满的时候并发

3.怎么回收

标记清除算法
image.png
先标记要回收的对象,在统一清除。缺陷是会产生大量不连续的内存碎片,在分配大对象时,不得不提早触发另外一次垃圾收集动做jvm

复制算法
image.png
将内存分AB两块,每次只用一块,A的内存用完了,回收的时候就将A还存活的对象放在B上,而后统一清理A。缺陷是对象多的时候浪费复制时间,对内存的开销也比较大。函数

标记整理算法
image.png
标记出全部要回收的对象,标记完成后,将存活的对象移动一端,而后清理掉端边界之外的内存。工具

分代收集算法
根据新生代和老年代的特色,使用分代收集算法
image.png
由于新生代朝生夕死,因此用复制算法,仅须要复制少许对象。
老年代存活率高,对象多,没有额外空间进行分配,就使用标记-整理算法。能够自由搭配不少种,不过大体的类型就是如下几种。

串行收集器(Serial)
image.png
-XX:+SerialGC
单线程收集器,这里的单线程不是指垃圾回收的线程只有一个,而是相对于应用程序来说,在回收垃圾的时候要暂停应用程序(STW)
在内存不足时,串行GC设置停顿标识,当全部线程到达安全点后,应用程序暂停,开始垃圾收集。适合堆内存不高且单核的cpu使用。

并行收集器(ParNew,Parallel Scavenge)
image.png
-XX:+UseParNewGC
ParNew是Serial收集器的多线程版本,搭配老年代CMS首选。适合多核cpu。ParallelScavenge更关注吞吐量,一样须要STW,适合和前台交互少的系统,后台处理任务量大的

并发收集器(CMS)
更关注低延迟的收集器,分为如下四个阶段,适合内存大,多核cpu。缺陷:消耗内存过的大,容易引发fullGC。有碎片,为防止fullGC,默认开启碎片整理参数
image.png

初始标记:以STW的方式标记全部根对象,很快
并发标记,与程序并发执行,标记出全部根路径的可达路径
从新标记,以STW标记有可能在这期间错过的,一样很快
并发清除,将不可达对象并发回收

G1收集器
引入了 Region概念,和CMS比较像,只不过有Region的优点
image.png

观看GC日志
image.png
33.125表明虚拟机启动到如今,通过了多少秒
Full GC和GC表明停顿类型,不是为了区分新生代和老年代的,若是有Full,表明是以SWT触发的垃圾收集
DefNew,Tenured,Perm才是区域,发生在什么区域上的
3324K - > 152K(3712K) :GC前该内存区域已使用的容量 -> GC后该内存已使用的容量(总容量)

内存分配与回收策略

  • 对象优先在Eden分配,若是说Eden内存空间不足,就会发生Minor GC
  • 大对象直接进入老年代,大对象须要大量连续内存空间的Java对象,好比很长的字符串和大型数组,一、致使内存有空间,仍是须要提早进行垃圾回收获取连续空间来放他们,二、会进行大量的内存复制。-XX:PretenureSizeThreshold 参数 ,大于这个数量直接在老年代分配,缺省为0 ,表示毫不会直接分配在老年代。
  • 长期存活的对象将进入老年代,默认15岁,-XX:MaxTenuringThreshold调整
  • 动态对象年龄断定,为了能更好地适应不一样程序的内存情况,虚拟机并非永远地要求对象的年龄必须达到了MaxTenuringThreshold才能晋升老年代,若是在Survivor空间中相同年龄全部对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就能够直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄
  • 空间分配担保:新生代中有大量的对象存活,survivor空间不够,当出现大量对象在MinorGC后仍然存活的状况(最极端的状况就是内存回收后新生代中全部对象都存活),就须要老年代进行分配担保,把Survivor没法容纳的对象直接进入老年代.只要老年代的连续空间大于新生代对象的总大小或者历次晋升的平均大小,就进行Minor GC,不然FullGC。

内存泄漏和内存溢出
内存泄漏是该释放的对象没有获得释放
内存溢出是撑爆了内存,对象太多了

JDK为咱们提供的工具

  • jps

列出当前机器上正在运行的虚拟机进程
-p :仅仅显示VM 标示,不显示jar,class, main参数等信息.
-m:输出主函数传入的参数. 下的hello 就是在执行程序时从命令行输入的参数
-l: 输出应用程序主类完整package名称或jar完整名称.
-v: 列出jvm参数, -Xms20m -Xmx50m是启动程序指定的jvm参数

  • jstat

是用于监视虚拟机各类运行状态信息的命令行工具。它能够显示本地或者远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据,在没有GUI图形界面,只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的首选工具。
假设须要每250毫秒查询一次进程2764垃圾收集情况,一共查询20次,那命令应当是:jstat-gc 2764 250 20
经常使用参数:
-class (类加载器)
-compiler (JIT)
-gc (GC堆状态)
-gccapacity (各区大小)
-gccause (最近一次GC统计和缘由)
-gcnew (新区统计)
-gcnewcapacity (新区大小)
-gcold (老区统计)
-gcoldcapacity (老区大小)
-gcpermcapacity (永久区大小)
-gcutil (GC统计汇总)
-printcompilation (HotSpot编译统计)

  • jmap
  • jstack
  • .........
相关文章
相关标签/搜索