1. JVM 分代模型及对象分配流转

1.1. 背景引入

从之前的文章中了解到,想要 Java 程序运行起来,要把相关类加载到方法区,每个线程通过字节码执行引擎、程序计数器和 Java 虚拟机栈来执行字节码指令,存储线程局部变量等,这个过程中产生的对象存放在 Java 堆内存中。

jvm_generation_model01

上述只是最基本的流程,如果系统一直运行,会不断产生新的对象,达到指定条件后,出发垃圾回收机制,会回收释放空间。

jvm_generation_model02

如果每次达到指定条件,都要去全局判断,也是很耗费性能的。这里 JVM 有一个优化的机制,将堆内存区域划分两种类型,新生代老年代

分新生代和老年代的好处是什么呢?

我们代码中有静态常量等等,生存周期会很长的对象,这种对象肯定还有引用,每次垃圾回收都去判断会损耗性能。

如果新生代经过很多次垃圾回收(15次)对象还会存活,JVM 会认为这个对象就是长期存活的对象,会把这种对象放在老年代中,这样下次新生代垃圾回收就不会再继续判断这种对象了,提升了新生代的垃圾回收性能。

jvm_generation_model03

1.2. 分代模型:新生代和老年代

通过上面介绍,知道 Java 系统中不同对象生存周期是不同的,根据生存时间划分为新生对象及老年对象。

新生对象数据存放区域称为新生代

老年对象数据存放区域称为老年代

最开始对象都在新生代中,躲过多次垃圾回收后,满足指定条件,就会从新生代流转到老年代中。

  • 注意:每躲过一次垃圾回收,可以说对象的年龄增加一岁。

新生代垃圾回收,称为Minor GC,也称为Young GC,一般方法执行完,出栈后,很对对象就没有引用了,变成垃圾对象,垃圾回收器不会立即进行回收,会运行一段时间,垃圾对象积攒很多,满足一定条件后,才会进行回收,这样减少回收频率,提升整体性能。

老年代垃圾回收,称为Major GC,也成为Full GC,当老年代达到某些条件时,也会进行垃圾回收,和新生代垃圾回收的思想方法有区别。

GC Roots

判断对象是否能回收,通过可达性算法来判定,该思想是判断对象都有谁在引用它,然后一层层向上判断,看是否有一个GC Roots。(在JVM规范中,局部变量就是可以作为GC Roots的静态变量也可以看做是一种GC Roots。)

只要对象被方法的局部变量、类的静态变量给引用了,就不会回收它们。

finalize()方法的作用

思考:在回收的时候,没有GC Roots引用的对象,一定立马被回收吗?

答:不是的,finalize()方法可以避免被立即回收。

public class ReplicaManager {
    public static ReplicaManager instance;
    @Override
    protected void finalize() throws Throwable {
        ReplicaManager.instance = this;
    }
}

如果一个对象要被垃圾回收了,这个对象重写了Object类中的finalize()方法。

会先尝试调用一下他的finalize()方法,看是否把自己这个实例对象给某个GC Roots变量。

如果重新让某个GC Roots变量引用自己,则该对象就不会被垃圾回收。

该方法平时很少用,了解即可。

1.3. 永久代及回收

永久代,顾名思义,代表的是“永久”存在的数据,指的就是我们的方法区。

但是这里也会有垃圾回收,并不是永久不回收的。当同时满足以下情况时,会进行垃圾回收。

  • 该类的所有实例对象,都已经从 Java 堆内存里被回收。
  • 加载这个类的 ClassLoader 已经被回收。
  • 对应类的 Class 对象没有任何引用。

1.4. Java 虚拟机栈

当栈内方法栈帧执行完毕后,会出栈,出栈后,方法及对应的局部变量会直接就从内存清理掉了,这里不会有专门的垃圾回收机制。

1.5. Java中对象不同的引用类型

关于引用和垃圾回收的关系,要注意新的概念,就是Java里有不同的引用类型。分别是强引用、软引用、弱引用和虚引用

比较常用的,是 强引用 和 软引用 ,强引用是绝对不能回收的对象,软引用是对象可有可无,如果内存实在不够了,可以回收掉。

强引用

一个变量引用一个对象,只要是强引用的类型,那么垃圾回收的时候绝对不会去回收这个对象的

public class Kafka {
    public static ReplicaManager replicaManager = new ReplicaManager();
}

软引用

实例对象用一个“SoftReference”软引用类型的对象给包裹起来了,此时这个引用就是软引用了。

正常情况下垃圾回收是不会回收软引用对象的,如果进行垃圾回收之后,发现内存空间还是不足,此时就会把软引用对象给回收掉,即使被变量引用了,也要被回收。

public class Kafka {
    public static SoftReference<ReplicaManager> replicaManager = 
            new  SoftReference<ReplicaManager>(new ReplicaManager());
}

弱引用

弱引用就跟没引用是类似的,如果发生垃圾回收,就会把这个对象回收掉。

public class Kafka {
    public static WeekReference<ReplicaManager> replicaManager = 
            new  WeekReference<ReplicaManager>(new ReplicaManager());
}

虚引用

虚引用,暂时忽略也行,因为很少用。

1.6. JVM 内存相关的几个核心参数

-Xms:Java堆内存的大小
-Xmx:Java堆内存的最大大小
-Xmn:Java堆内存中的新生代大小,扣除新生代剩下的就是老年代的内存大小了
-XX:PermSize:永久代大小
-XX:MaxPermSize:永久代最大大小
-Xss:每个线程的栈内存大小

jvm_memory_core_params01

results matching ""

    No results matching ""