java垃圾回收机制
介绍
GC虽然已经自动帮我们完成清理,但了解一下java垃圾回收机制,还是对我们性能调优,问题排查等有很大帮助。本文为博主阅读网上资料后做的相关整理。
JVM内存区域
1.虚拟机栈:每个方法被执行的同时会创建栈桢 ,要保存执行方法时的局部变量表、操作数栈、动态连接和方法返回地址等信息,方法执行时入栈,方法执行完出栈,出栈就相当于清空了数据,入栈出栈的时机都很明确,此处不需要进行 GC。
2.本地方法栈:虚拟机栈为虚拟机执行 Java 方法时服务,本地方法栈为虚拟机执行本地方法时服务,此处也不需要进行 GC。
3.程序计数器:线程独有。可以看作是当前线程执行的字节码的行号指示器。Java虚拟机的多线程是通过线程轮流切换并分配处理器的时间来完成的。在任何一个时刻,一个处理器只会执行一个线程,如果这个线程被分配的时间片执行完了(线程被挂起),处理器会切换到另外一个线程执行,程序计数器即记录唤醒线程时上次执行到的位置。另外程序计数器是唯一一个 Java 虚拟机规范中没有规定任何 OOM 情况的区域。此处也不需要进行 GC。
4.本地内存:即堆外内存,包含元空间和直接内存,JAVA8之后此区域也不需要进行 GC。
5.堆:对象实例和数组都是在堆上分配,GC主要针对这些数据进行回收,堆是java垃圾回收处理的主要区域。
系统垃圾识别
GC如何判断堆中对象实例等是不是垃圾?
引用计数法
为对象添加引用次数属性,被引用一次,即加一。未被引用即为0,代表可被回收。
但此类无法解决循环引用的问题,在互相被引用的对象要求释放时,因为引用次数不为零,造成无法回收。因此此方法用得较少。
可达性算法
利用一个GC Root的对象为起点出发,逐步指向下一个可达的节点,直至所有节点遍历完毕,不在生成的这个引用链上的对象,即被判断为垃圾,会被GC回收。
另外,不是a, b 对象可回收,就一定会被回收。对象的 finalize 方法给了对象一次垂死挣扎的机会,当对象不可达(可回收)时,当发生GC时,会先判断对象是否执行了 finalize 方法,如果未执行,则会先执行 finalize 方法,我们可以在此方法里将当前对象与 GC Roots 关联,这样执行 finalize 方法之后,GC 会再次判断对象是否可达,如果不可达,则会被回收,如果可达,则不回收。且finalize 方法只会被执行一次,如果第一次执行 finalize 方法此对象变成了可达确实不会回收,但如果对象再次被 GC,则会忽略 finalize 方法,对象会被回收。
GC Root是什么?哪些对象可作为GC Root?
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中 JNI(即一般说的 Native 方法)引用的对象
垃圾回收主要方法
通过可达性算法识别了哪些数据是垃圾,如何对垃圾进行回收?主要有以下三种方法。
标记清除算法
根据可达性算法标记出所有的可回收对象
对可回收对象进行回收
此种算法回收之后会造成很多零碎的小内存,不能满足连续内存的需要
复制算法
将堆分为两块区域,A,B,区域A分配对象,B不分配对象
对A使用标记法把存活的对象都标记出来,并将它们复制到区域B
把A中对象全部清理释放空间
此方法解决了内存碎片的问题,缺点是堆空间大小利用率大大降低,复制也有资源浪费。
标记整理法
根据可达性算法标记出所有的可回收对象
对标记对象进行整理,将所有存活对象都往一端移动,紧密排列
清除掉另一端所有非存活对象
当前垃圾回收器对比
说完了垃圾回收的方法论,到了垃圾回收器就是内存回收的具体实现了,当前垃圾回收器按使用时期分为如下几类:
JVM中的堆,一般分为三部分:新生代,老年代,永久代(JAVA8永久代移除,被元空间取代)
1.何为新生代?
主要用来存放新生对象,一般占堆空间的三分之一空间,会频繁创建对象,频繁触发GC进行垃圾回收。
新生代又分为三个区: Eden区、ServivorFrom、ServivorTo。
Eden区:Java新对象的出生地,当此区内存不够时即触发GC,对新生代区进行一次垃圾回收。
ServivorTo:保留一次GC过程中的幸存者
ServivorFrom:上一次GC的幸存者,作为本次GC的被扫描者。
本区GC一般采用复制算法。
2.何为老年代?
当新生代无法找到足够大连续空间给新创建的较大对象时,会在老年代触发GC,一般采用标记清除算法。当老年代也没有空间时,会抛出OOM异常。
新生代回收器
在新生代工作的垃圾回收器:Serial, ParNew, ParallelScavenge。
老年代回收器
在老年代工作的垃圾回收器:CMS,Serial Old, Parallel Old
新生代回收器和老年代回收器一般配合使用,共同完成JVM中GC。
还有一个驾驭一切的垃圾回收器,在新生代,老年代都能使用,即G1。
总结
- 垃圾回收机制回收JVM堆内存里的对象空间,不负责回收栈内存数据。
- 对其他物理连接,比如数据库连接、输入流输出流、Socket连接无能为力。
- 垃圾回收发生具有不可预知性,程序无法精确控制垃圾回收机制执行。
- 可以将对象的引用变量设置为null,暗示垃圾回收机制可以回收该对象。
- 现在的JVM有多种垃圾回收实现算法,表现各异。
- 垃圾回收机制回收任何对象之前,总会先调用它的finalize方法(如果覆盖该方法,让一个新的引用变量重新引用该对象,则会重新激活对象)。
- 我们可以通过System.gc()或者Runtime.getRuntime().gc()来通知系统进行垃圾回收,会有一些效果,但是系统是否进行垃圾回收依然不确定。永远不要主动调用某个对象的finalize方法,应该交给垃圾回收机制调用。
本博客所有文章除特别声明外,大部分为学习心得,欢迎与博主联系讨论