一.JAVA中的的四种引用类型
1.强引用(Strong Reference)
java中默认声明的就是强引用,只要引用存在,垃圾回收器将永远不会回收被引用的对象,哪怕内存不足,jvm抛出OutOfMemoryError也不会回收;如果想中断强引用与对象之间的联系,可以显示的将强引用赋值为null,这样一来,JVM就可以适时的回收对象了
2.软引用(Soft Reference)
用来描述一些非必须但是有用的对象,在内存足够的情况下,引用不会被回收,只有在内存不足的情况下,系统会回收软引用对象 ,如果回收之后任然没有足够的内存,才会抛出OutOfMemoryError;
3.弱引用(Weak Reference)
无论内存足够与否,只要jvm进行垃圾回收,那些被弱引用关联的对象都会被回收.
4.虚引用(Phantom Reference)
虚引用是最弱的一种引用关系,它随时可能会被回收.
二.垃圾回收算法
1.堆信息查看
idea中运行一个main方法
运行main方法之前,添加一个vm参数 -XX:+PrintGCDetails 然后运行main()方法,控制台打印以下堆信息;
从打印信息中可以看出,堆的大致结构如下:
永久存储区在jdk1.8以后被称为元空间(元空间逻辑上存在,物理上不存在)
2.GC信息查看
GC信息查看我们需要手动的去创造一个能内存溢出的程序,并且将堆内存调小,方便快速进行GC
内存溢出代码:
public static void main(String[] args) {
String s = "21412512321312371783";
for(;;){
s+=s;
}
}
添加main方法的运行参数:-Xms8m -Xmx8m -XX:+PrintGCDetails
控制台输出的GC信息
可以看出,抛出内存溢出之前进行了6次轻GC,3次重GC
3.GC过程 内存回收的机制概括来说就是分代分配,分代回收.
GC方式:
Minor GC或者Young GC : 轻GC 发生在新生区 eden区满则发生 清除eden中的消亡对象
Full GC:重GC 触发条件是老年区内存满,执行fullGC后 新生区被清理干净,老年区不一定能清理干净.最后执行Full GC以后,老年区还是满的则会抛出OOM;
对象被创建时,内存的分配首先发生在年轻代的eden区,(大对象可以直接被创建在老年代),大部分的对象在创建后很快就不会再使用了,因此很快变得不可达,于是被年轻代的GC机制清理掉.这个GC机制被称为Minor GC或者Young GC
GC过程
1.伊甸园区内存满,执行Minor GC 将消亡的对象清理掉,并将存活下来的对象复制到TO区(to区为永远空白的区域),from区的对象也复制到TO区,此时伊甸园区与之前的from区都是空的,之前的from区空白后变成to区,
2.对象经过多次Minor GC后在from区依然存活并且满足大于默认值15次,就会被移到老年区.(HotSpot虚拟机默认15次,用-XX:MaxTenuringThreshold控制,大于该值进入老年代)
3.当from区满 from区的存活对象会被移到老年区(这个结论是我瞎几把猜的 感觉不靠谱)
4.GC算法
1.标记清除法
标记阶段: 存活的对象打上标记
清除阶段: 清除没有标记的对象
优点: 实现简单,不需要额外的空间
缺点: 会产生内存碎片,扫描2次,时间成本高
2.复制算法
把存活的对象拷贝到一边,剩下的全部清除,适用于存活率较低的区域(eden区)
优点: 扫描一次效率高,并且不会产生内存碎片.
缺点: 浪费额外的空间(to,from)
3.标记整理(标记压缩)
标记阶段: 存活的对象打上标记
清除阶段: 清除没有标记的对象 并且将存活的对象向前移动,
缺点:效率低
优点:没有内存碎片
4.引用计数法(java未使用此方法)
每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。
优点: 此方法简单,
缺点: 但无法解决对象相互循环引用的问题,计数器浪费额外空间