1.哪些内存需要回收?
2.什么时候回收
3.如何回收
先理解引用,分为强软弱虚
强 就是 new这种,只要引用在,gc不会回收
软 就是还有用非必需,若内存溢出则先回收,若还溢出则抛出内存异常 SoftReference类实现软引用
弱 非必需 不管如何下次gc开启就会被回收 无关内存 WeakReference类实现弱引用
虚 幻影引用/幽灵引用 只是在采用虚引用被gc回收时收到一个系统通知 PhantomReference类实现虚引用
--1.引用计数算法
对象中加个引用计数器,引用+1,失效-1,为0回收
--2.可达性分析算法
通过GC Roots 的对象作为起始点,从节点向下搜索,搜索走过的路径为引用链,一个对象到GC Roots无引用链相接就可回收
GC Roots 可为 栈中引用对象,方法区内类静态属性引用对象,方法区常量引用对象,本地方法栈的Native引用对象
不可达的对象也不是必死,必须经过两次标记。当不可达时,会标记一次并进行一次筛选,如果对象没有覆盖finaliza()或者调用过finaliza()则虚拟机将视为不必要执行
如果判断需要执行,会将这个对象放到一个F-Queue的队列中,等待虚拟机自动创建的低优先级的Finalizer线程去触发对象的finaliza()但不保证会等代他运行结束,如果在finaliza()重新与引用链上任意对象建立关联就可以在GC对F-Queue进行的第二次小范围标记中拯救自己,移除即将回收列表,因此就算finaliza()真的被执行了但是还是可以存活的
但是 任何一个对象的finaliza()只会执行一次。
以上是对于堆上的回收,对于方法区,会回收废弃常量与无用的类,废弃常量就是没任何对象引用这个常量,无用类则是该类所有实例被回收,加载该类的ClassLoader已经被回收,该类的java.lang.Class对象在任何地方无引用,无法通过反射访问。
垃圾收集算法
1.标记-清理算法 Mark-Sweep
标记需要回收对象,统一回收所有被标记的。效率低,清除后产生大量不连续的内存碎片。大对象进入后无连续的内存就会触发GC。
2.复制算法Copying
空间55分,清理已使用的那一半的全部,可用的复制到另一半内存。
3.标记-整理算法 Mark-Compact
标记回收对象,然后向一段移动,清除掉端边界以外的内存。
4.分带收集算法
分为新生代,老年代,新生代又包含Eden和两个survivor分别为from和to。当触发GC时新生代采用copying将存留的对象copy到to,eden和from为空,此时from和to的身份互换。当from中的对象熬过一次GC则年龄计数器+1,到达阈值后放入老年代。
内存分片与回收策略
在堆上,对象主要在eden上,如果启动本地线程分配缓存则优先放在TLAB,也有极少数情况放在老年代。这个都看用的什么收集器。
对象优先放在Eden,如果满了的话会触发GC,当大对象进入后,survivor放不下则会进入老年代,长期存活也会进入老年代,当然同年的对象如果大于survivor的一半也会进入老年代。大对象是直接进入老年代的,但是老年代的本身不一定够,要每次回收老年代对象容量的平均大小作为平均值与剩余空间比较,决定是否Full GC腾出更多空间。
下面来记录一下手动触发GC的方法,上面讲了Eden满了的时候是会触发GC回收的,手动的话有两种,System.gc()和Runtime.getRuntime.gc().其实System.gc()内部也是Runtime的方法。
GC的话还要了解下收集器
Serial 收集器 stop the world 这个开启的时候其他都要停下来,所以尽量减少
ParNew 收集器 多线程版Serial 多核的时候选这个 单核还是Serial快
Parallel Scavenge 收集器 关注吞吐量 新生代选这个老年代必须选Serial old
Serial old 收集器 老年代的
Parallel old 收集器 老年代的
CMS 收集器 基于标记-清除算法
G1 收集器 最新
收集器相关这里全
https://blog.csdn.net/hudashi/article/details/52058355