JVM垃圾回收

Java垃圾回收

垃圾回收需要完成三件事:

  • 那些内存需要回收
  • 什么时候回收
  • 怎么而回收

1、 那些内存需要回收

1.1、 垃圾对象的判断

垃圾收集器会对垃圾对象进行回收。那么什么样的对象是垃圾对象?一般来讲,没有引用的对象可以称之为垃圾对象。对垃圾对象的判断主要有两种方法:引用计数法可达性分析。由于引用计数法有很多额外的例子需要考虑(比如两个对象之间的循环引用),必须配合大量额外的处理才能保证正确的工作,所以Java采用的是可达性分析算法。

可达性分析的基本思路就是通过一系列成为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索。如果某个对象到GC Roots间没有任何引用链相连,则证明此对象不可达。

在Java中,固定可作为GC Roots的对象包括以下几种:

  • 虚拟机栈中引用的对象(比如各个线程被调用的方法堆栈中所使用到的参局部变量、临时变量等)
  • 在方法区中类静态属性引用的对象(比如Java类的引用类型静态变量)
  • 在方法区中常量引用的对象(比如字符串常量池里的引用)
  • 在本地方法栈JNI(即通常所说的Native方法)引用的对象
  • Java虚拟机内部的引用
  • 所有被同步锁(synchronized关键字)持有的对象
  • 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等

1.2、 引用的类型

可达性分析中很重要的一点就是对象是否被引用,而在JDK1.2后,Java将引用的概念进行了拓展,将引用分为强引用、软引用、弱引用、虚引用

  • 强引用

    最传统的“引用”定义,类似Object obj=new Object()这种引用关系。只要强引用关系还在,垃圾收集器就不会回收被引用的对象

  • 软引用

    软应用被用来描述一些还有用、但非必须的对象。只被软引用关联的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常

  • 弱引用

    用来描述非必须的对象,比软引用强度更弱。被软引用关联的对象只能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象

  • 虚引用

    最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用取得一个对象的实例。虚引用唯一的目的就是在对象被回收时收到一个系统通知

2、什么时候回收

2.1、内存分代策略

对象的内存分配,从概念上将,应该都是在堆上分配(实际上也有可能经过即时编译后被拆散成标量类型并间接在栈上分配)。依据经典分代的设计,Java堆被划分为两个区域:新生代(Young Generation)和老年代(Old Generation)两个区域。其中,新生代又被划分为一块较大的Eden空间和两块较小的Survivor空间,默认下Eden和两块Survivor的大小比例是8:1:1。

新生对象通常会分配在新生代,少数情况下(例如对象大小超过一定阈值)也可能会直接分配在老年代

2.2、对象分配与GC

大多数情况下,新对象在新生代的Eden去中分配。当Eden区没有足够空间分配时,虚拟机将发起一次Minor GC。若存活的对象能够放进Survivor区,则将其移入;若不行,则将存活的对象放入老年代中。新对象若在GC后还不能放进Eden区中,则直接放进老年代

如果通过-XX:PretenureSizeThreshold参数指定了大小,则大于这个大小的对象会被直接放进老年代中,避免了在Eden和两个Survivor去之间来回复制,产生大量的内存复制操作

每个对象的对象头中有一个对象年龄计数器。如果经过一个Minor GC且能被Survivor容纳的话,其年龄就加一。当对象的年龄增加到一定的程度(默认是15),就会被转入老年代中

一旦大量对象在Minor GC后还存活(最极端的情况是,新生代所有的对象都存活),且这些对象无法被Survivor所容纳,那么这些对象就会被送入老年代。所以,在发生Minor GC之前,虚拟机必须检查老年代的最大可用连续空间是否大于新生代所有对象的总空间。若满足条件,则这次Minor GC是安全的。如果不满足,虚拟机会先查看-XX:

posted @ 2020-12-27 23:15  maurrinho  阅读(3)  评论(0编辑  收藏  举报