JVM之垃圾回收相关概念
System.gc()的理解
- 在默认情况下,通过system.gc()或者Runtime.getRuntime().gc()的调用,会显式触发Full GC,同时对老年代和新生代进行回收,尝试释放被丢弃对象占用的内存
- System.gc()调用附带一个免责声明,无法保证对垃圾收集器的调用,即提醒jvm的垃圾回收器执行gc,但不确定是否马上执行gc
内存溢出(OOM)
没有空闲内存,并且垃圾收集器也无法提供更多内存
内存不够的情况:
1.Java虚拟机的堆内存设置不够:可能存在内存泄漏问题,或者可能堆的大小不合理
2.代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)
内存泄漏:
只有对象不会被程序用到了,但是GC又不能回收他们的情况,才叫内存泄漏
举例:
1.单例模式:单例的生命周期和应用程序是一样长的,所以单例程序中如果持有对外部对象的引用的话,那么这个外部对象是不能被回收的则会导致内存泄漏的产生。
2.一些提供close的资源未关闭导致内存泄漏:数据库连接,网络连接和io连接必须手动close,否则不能被回收
Stop The World
GC事件发生过程中,会产生应用程序的停顿,停顿产生时整个应用程序线程都会被暂停,没有任何响应。
被STW中断的应用程序线程会在完成GC之后恢复,频繁中断会造成用户体验查,所以要减少STW的发生。
例:可达性分析算法中枚举根节点(GC Roots)会导致所有的Java执行线程停顿,所以分析工作必须在一个能确保一致性的快照中进行,,一致性指整个分析期间整个执行系统看起来像被冻结在某个时间点上,如果出现分析过程中对象引用关系还在不断变化,则分析结果的准确性无法保证
STW是JVM在后台自动发起和自动完成的,开发中不要使用System.gc()会导致stw发生
垃圾回收的并发和并行:
并行:指多条垃圾收集器并行工作,但此时用户进程仍处于等待状态
串行:单线程执行,如果内存不够,则程序暂停,启动JVM垃圾回收器进行垃圾回收,回收完再启动程序的线程。
并发:用户与垃圾收集线程同时执行,(不一定并行,可能会交替执行),垃圾回收线程在执行时不会停顿用户程序的运行。
安全点:
在程序执行时,只有特定位置才能停顿下来开始GC,这些位置称为安全点,如:选择一些执行时间较长的指令作为安全点,方法调用,循环跳转何异常跳转等。
如何在GC发生时,检查所有线程都跑到最近的安全点停顿下来呢?
抢先式中断:(目前没有虚拟机采用了)
首先中断所有线程,如果还有线程不在安全点,就恢复线程,让线程跑到安全点
主动式中断:
设置一个中断标志,各个线程运行到安全点的时候主动轮询这个标志,如果中断标志为真,则将自己进行中断挂起。
安全区域:
指在一段代码片段中,对象的引用关系不会发生变化,在这个区域中任何位置开始GC都是安全的
这个是为了解决程序“不执行”的时候,例如线程处于sleep或blocked状态,这时候无法响应JVM的中断请求。
实际执行过程:
1.当线程运行到安全区域时,首先标识已经进入了安全区域,如果这段时间内发生GC,JVM会忽略标识为安全区域的线程
2.当线程即将离开安全区域时,会检查JVM是否已经完成GC,如果完成了,则继续运行,否则线程必须等待直到收到可以安全离开安全区域的信号为止
引用
引用分为强引用,软引用,弱引用,虚引用,这4种引用强度依次逐渐减弱
强引用:指程序代码中普遍存在的引用赋值,无论任何情况下,只要强引用关系还在,垃圾收集器就永远不会回收掉被引用的对象。----不回收
强引用的对象是可触及的,垃圾收集器就永远不会回收掉被引用的对象,是造成Java内存泄漏的主要原因之一
软引用:在系统将要发生内存溢出之前,将会把这些对象列入回收范围之中进行第二次回收,如果这次回收后还没有足够的内存,才会抛出内存溢出异常-------内存不足即回收
通常用来实现内存敏感的缓存,比如:高速缓存
垃圾回收器在某个时刻决定回收软可达对象的时候,会清理软引用,并可选地把引用存放到一个引用队列。
Object obj=new Object();//声明强引用
SoftReference<Object> sf=new SoftReference<Object>(obj);
obj=null;//销毁强引用
弱引用:被弱引用关联的对象只能生存到下一次垃圾收集之前,当垃圾收集器工作时,无论内存空间是否足够,都会回收掉被弱引用关联的对象
当弱引用对象被回收时,就会加入指定的引用队列,通过这个队列可以跟踪对象的回收情况,软引用和弱引用都被用于保存可有可无的缓存数据
Object obj=new Object();//声明强引用
WeakReference<Object> wf=new WeakReference<Object>(obj);
obj=null;//销毁强引用
虚引用:一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用获得一个对像的实例。作为一个对象设置虚引用关联的唯一目的就是能够在这个对象被收集器回收时受到一个系统通知,从而追踪垃圾回收器。
虚引用必须和引用队列一起使用。虚引用在创建时必须提供一个引用队列作为参数,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象后,将这个虚引用加入引用队列,以此通知程序对象的回收情况,
由于虚引用可以跟踪对象的回收时间,因此,也可以将一些资源释放操作放置在虚引用中执行和记录。
Object obj=new Object();//声明强引用
ReferenceQueue phantomQueue=new ReferenceQueue();
PhantomRererence<Object> pf=new PhantomReference<Object>(obj,phantomQueue);
obj=null;//销毁强引用
终结器引用(Final reference)
它用以实现对象的finalize()方法,也可以称为终结器引用;在GC时,终结器引用入队,由Finalizer线程通过终结器引用找到被引用对象并调用它的finalize()方法,第二次GC时才能回收被引用对象。
JVM之对象的实例化,直接内存和执行引擎