System.gc();
Runtime.getRuntime().gc();
通过上面两个方法的调用,会显式的触发Full GC,同时对年轻代和老年代进行回收,尝试释放被丢弃对象所占用的内存
System.gc() 无法保证对垃圾收集器的调用,无法确定调用时间和能否真正进行垃圾回收,但是一般情况下,垃圾回收应该是自动进行的,无需手动触发,除非在一些特殊情况下,例如性能测试前可以调用System.gc()
System.runFinalization(); 会强制调用失去引用对象的finalize() 方法
内存溢出:没有空闲的内存,并且垃圾回收器也无法提供更多内存(回收以后,依然不够)
随着GC的发展,一般情况下不太容易出现内存溢出,除非应用程序占用内存的速度超过垃圾回收的速度,
大多数情况下,会对不同的年龄段分段进行回收,但在特殊情况下,也会就像独占式的Full GC,一次回收大量内存
产生内存溢出的原因:
堆的内存过小
代码中有大量的大对象,并且长时间不能被回收
另外关于方法区的内存溢出,因为方法区的垃圾回收是很不积极的,所以在1.7及以前采用虚拟机内存的永久代更容易内存溢出,1.8及以后,采用本地内存的元空间,就不太容易出现元空间的内存溢出了
在抛出oom之前,通常垃圾收集器会被触发,尽可能去清理出空间,当然也不是任何情况下都会触发垃圾回收器,例如有一个超大对象超过了堆的最大值,就直接报oom了
内存泄漏:严格来说,只有对象不会再被程序用到了,但是GC又不能回收他们,就叫做内存泄漏
而实际情况中,如果存在生命周期很长的对象(例如静态的类变量)导致oom,也可以叫做宽泛意义上的"内存泄漏"
内存泄漏会导致可用的内存越来越少,最终由可能会导致omm,使程序崩溃
例如:指针忘记断开,导致一部分对象仍然可达,无法回收
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rpOb3Dm7-1661178661192)(C:\Users\IsTrueLove\AppData\Roaming\Typora\typora-user-images\image-20220817215353749.png)]](https://1000bd.com/contentImg/2023/06/15/180444689.png)
需要注意的是,java没有使用引用计数算法,所以不会产生循环引用导致的内存泄漏
java可能产生内存泄漏的场景:
stop the world,简称STW,在GC过程中,整个应用程序线程都会被暂停,产生的程序停顿,没有任何响应,有点像卡死的感觉
stw 中断的应用程序线程会在完成GC后恢复,但是频繁的中断用户体验会很差,所以需要尽量减少stw的发生
stw和哪个GC无关,所有的GC都有这个事件,只是随着GC的发展,会尽可能的缩短暂停事件
stw是jvm后台自动发起和自动完成的,在用户不可见的情况下,把用户正常工作的线程全部停掉
所以开发中,一般不要调用System.gc();会导致垃圾回收,继而导致stw
程序中的并行、并发:
并发:一个时间段内同时发生
并行:一个时间点上同时发生
垃圾回收的并行、并发:
并行(多个垃圾回收线程同时执行)、并发(一个时间段内,用户线程和垃圾回收线程同时执行)、串行(单线程)
安全点、安全区域
并不是在任何时间点上,应用程序都可以停下来GC,需要在特定的时间点上才能停下来GC,这些时间点,就被称为安全点(safepoint)
safe point 选择很重要,太少会导致GC等待时间太长,太频繁又会导致运行时性能有问题,一般来说,大部分指令的执行时间很短,是否具有让程序长时间执行,是选择安全点的标准,例如方法调用,循环跳转等
如何在GC发生时,如何检查所有线程都跑到最近的安全点停顿下来:
但是对于已经挂起或者阻塞的线程,就没办法要求线程执行到安全点,所以就划分了安全区域
安全区域是指在一段代码片段中,对象的引用关系不会发生变化,在这个区域中任何位置开始GC都是安全的,可以看做是安全点的扩展
有这样一类对象,当内存空间足够的时候,可以保留在内存中,如果内存空间不足在进行垃圾回收后依然很紧张,就可以抛弃这些对象
强引用、软引用、弱引用、虚引用的区别,以及分别的使用场景
从jdk 1.2 以后,java对引用的概念进行了扩充,将引用分为了强引用、软引用、弱引用、虚引用四种,四种引用的强度依次衰弱,除了强引用以外,其他三种引用都继承了抽象类 Reference
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sJEj81dY-1661178661193)(C:\Users\IsTrueLove\AppData\Roaming\Typora\typora-user-images\image-20220820174235270.png)]](https://1000bd.com/contentImg/2023/06/15/180444632.png)
Reference 的四个实现类,就是软、弱、虚以及终结器引用
以下几种情况,都是在引用还在,对象可达的情况下,不可达的对象是肯定要回收的
需要注意的同一个对象可能被多个对象引用,也就是说,可能同时被强、软、弱、虚引用
最常见的对象,正常创建出来的对象都是强引用,只要强引用对象可触及就不能被垃圾回收掉,所以强引用是造成java内存溢出的主要原因之一
软引用是来描述一些还有用,但是非必须的对象,只被软引用关联的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收没有足够的内存,才会抛出内存溢出异常,所以软引用是不会造成内存溢出的(内存溢出和强引用以外的引用无关)
软引用通常用来实现内存敏感的缓存,比如高速缓存就用到了软引用,如果还有空闲空间就暂时保留,如果没有就清理掉,这样就保证了使用了缓存的同时,不会耗尽内存
内存足够,不会回收软可达对象,只有内存不够时才会回收软可达对象
创建一个软引用对象,下面两种情况是相等的
SoftReference softReference = new SoftReference(new String("123"));
String s = new String("123");
SoftReference so = new SoftReference(s);
s = null; //取消强引用
弱引用:也是用来描述非必须对象,只被弱引用关联的对象,只能生存到下一次垃圾回收,只要发现弱引用,不管空间是否充足都会回收,但是因为垃圾回收器的线程优先级通常情况下很低,因此弱引用也有可能存在较长时间
弱引用也可以用来做缓存,弱引用和软引用的区别在于,当GC进行回收时,软引用需要判断是否回收,而弱引用是直接回收,所以弱引用对象更容易、更块的被GC回收掉
WeakReference weakReference =new WeakReference(new String("123"));
例如:WeakHashMap,
public class WeakHashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V> {
也称为“幽灵引用”或者“幻影引用”,是所有引用类型中最弱的一个
一个对象是否有虚引用,完全不会决定对象的生命周期,如果一个对象只有虚引用,那么几乎和没有引用是一样的,随时都能被垃圾回收器回收掉,唯一的作用就是跟踪垃圾回收的过程,能在对象被收集器回收时起到一个系统通知
//虚引用必须要创建一个引用队列
ReferenceQueue referenceQueue =new ReferenceQueue();
PhantomReference phantomReference =new PhantomReference(new String("123"),referenceQueue);
用以实现finalize()方法,无需手动编码,配合引用队列使用,在GC时,终结器引用进入队列,由finalize线程通过终结器引用找到被引用对象并调用finalize()方法,第二次GC时才能被回收