1.System.gc()的理解
- 默认情况下,调用
System.gc()
或Runtime.getRuntime().gc()
会显示触发Full GC
(对新生代和老年代同时进行回收) - 该方法被调用后并不能确保对垃圾回收器的调用,只是提醒垃圾回收器执行GC
2.内存溢出和内存泄漏
-
内存溢出:没有空闲内存,并且垃圾回收器也无法提供更多内存
- 导致内存溢出的原因:
- JVM堆的内存设置不足
- 代码中创建了大量对象,且因为存在引用所以一直未被垃圾回收器收集
- 在OOM前通常会触发垃圾回收,但是如果对象过大直接超出堆的最大值,JVM会认为垃圾回收无法处理,直接抛出OOM
- 导致内存溢出的原因:
-
内存泄漏:对象不会被使用,但是GC又不能回收这些对象,比如在类中声明了很多静态变量(随着类的消亡而消亡),这些对象的生命周期较长,无法在短期被回收,最终导致了OOM
- 举例:
- 单例模式:如果某个单例的对象对另一个对象存在引用的话,由于单例对象中存在静态属性,因为着该单例对象随着程序的结束才会被回收,所以即使另一个对象不被使用,因为存在单例对象的引用,也无法被回收
- 数据库连接、网络连接(连接会产生许多对象)未进行手动地关闭(调用close方法),需要等到程序结束才会被回收
- 举例:
3.Stop The World
- 指在GC时会产生程序的停顿(整个应用程序线程都会暂停)
- STW是JVM在后台自动发起和自动完成的
- 可达性分析中因为需要枚举
GC Roots
,也会产生STW(没停止程序则可能会产生新的根节点) - 所有的GC都会产生STW
- 开发中不要使用
System.gc
(会导致STW)
4.垃圾回收的并行和并发
-
操作系统中的并行和并发:
- 并发:在一个时间段中几个程序处于已启动和运行完毕间,且这些程序都运行在一个CPU上(即CPU把一个时间段划分为几个时间片段,并在这些片段间来回切换)
- 并行:系统有多个CPU,每个进程在一个CPU上运行,进程间不抢占资源,可同时进行
- 对比:
- 并发指多个任务在同一个时间段内同时发生;并行指多个任务在同一个时间点上同时发生
- 并发多个任务会抢占资源;并行多个任务不会抢占资源
- 并发发生在单CPU单核中;并行发生在多CPU或单CPU多核中
-
垃圾回收的并行核并发:
- 并行:指多条垃圾回收线程并行工作,但此时用户线程依旧处于等待状态;对应的另一个概念是串形(即单线程执行,如果内存不足则将当前程序暂停,启动JVM垃圾回收器进行GC,等回收完再启动程序)
- 并发:指用户线程和垃圾回收线程同时进行(即垃圾回收线程不会暂停用户线程),当然还是可能为交替执行的
5.安全点和安全区域
-
安全点:程序执行时并不是在任意位置都可以停下来进行GC,只有在特定位置可以,这些位置称为安全点
-
安全点少则导致GC等待时间长;安全点太多则导致运行时的性能问题(意味着需要不断切换)
-
GC时如果保证所有线程在最近的安全点停顿下来了?
- 抢先式中断:如果还有线程不在安全点,就恢复线程,让线程执行到安全点
- 主动式中断:设置一个中断标志,每个线程运行到安全点就轮询该标志(中断标志为真时线程就将自己挂起)
-
-
安全区域:在一段代码中,对象的引用关系不发生变化,在这个区域任意位置进行GC都是安全的
6.引用
如果希望描述某些对象(比如缓存):当内存空间足够时,可以将这些对象保留在内存中;如果空间在GC后还是不足,则将这些对象进行抛弃(之前的描述中是考虑没有引用就选择抛弃,不管内存是否足够)
注意:下面处理方式都是针对对象可达情况下的处理方式,对于不可达的即使是强引用也会被回收
6.1 强引用-不回收
- 最常见的普通对象引用(即
A a = new A()
),也是默认的引用类型 - 只要强引用(可达)还存在,JVM宁愿抛出OOM,垃圾回收器永远不会回收被引用的对象(当然如果没被引用或将引用赋值为null,对象还是要被回收的)
- 该引用是内存泄漏的主要原因之一
6.2 软引用-内存不足即回收
- 如果将不可达的对象进行回收后依旧内存不足,则会将软引用的对象(依旧可达)进行GC(回收完后还是内存不足就报OOM)
- 该引用不是内存泄漏的原因(内存不足时该引用的都被回收了,再造成OOM就不是软引用的问题了)
- 对于内存敏感的缓存会使用该引用,如高速缓存(有空闲空间就保留缓存;不足时会及时清理)
6.3 弱引用-发现即回收
- 只被弱引用关联的对象(依旧可达)只能生存到下一次GC前。GC时即使内存足够也会被回收(即使还存在弱引用导致对象还是可达的)
- 与软引用的区别:软引用在GC回收时还要判断内存是否足够;弱引用内存足够也回收,回收更快
6.4 虚引用-对象回收跟踪
- 无论虚引用是否存在都不会对对象的生命周期造成影响(相当于对象不存在该引用)
- 无法通过该引用获得对象实例,为对象设置该引用的主要目的是在该对象被GC时可以受到系统通知
- 该引用必须和引用队列联合使用