方法区

java静态类存储在那个空间 java静态数据存放在方法区_java静态类存储在那个空间


还是这个示意图,方法区有什么作用?方法区就是存放常量、静态变量、类信息的地方。

我们的Java代码,运行之后,通过类装载子系统装载到运行时数据区,其实主要就是加载到方法区中,这也就是类信息。然后再进行一系列操作,而这一切字节码的执行,都是由字节码执行引擎完成的。

常量和静态变量就不用过多解释。

在上一篇中的代码:

package com.hpu.dong;

public class test {
    public static final int m = 100;
    public static User user = new User();

    public test() {
    }

    public int add() {
        int a = 1;
        int b = 2;
        int c = (a + b) * 10;
        return c;
    }

    public static void main(String[] args) {
        test t = new test();
        t.add();
        System.out.println("test");
    }
}

可以看到user是个静态变量,那么它是怎么存储的?我们知道,对象是存放到堆中,那么方法区和堆之间有什么关系?

java静态类存储在那个空间 java静态数据存放在方法区_方法区_02


user存放在堆中,是有具体位置的,而方法区的内存空间中存放的则是指向堆中的user地址的指针。不仅方法区如此,栈其实也是如此,都是存放的堆内存中内容的地址。大概示意图就是这样:

java静态类存储在那个空间 java静态数据存放在方法区_jvm_03


反正就是栈和方法区都指向堆。

本地方法栈

先来看个代码:

new Thread().start();

点进start方法,再点进this.start0();可以看到:

private native void start0();

这就是一个本地方法。它最终的实现不是由Java代码完成的,而是由C语言代码完成的。可以简单理解为调用别的语言的方法。


我们来看一下堆的结构示意图:

java静态类存储在那个空间 java静态数据存放在方法区_方法区_04

堆内各部分内存是如何分配的?从图中可以看到,可以分为老年代和年轻代,这两个部分占据了堆的所有内存,并且两者内存按照一比二的比例进行分配,而年轻代中可以分为Eden区、From区、To区等,这些是按照8:1:1的比例进行分配。当然了,这些比例是可以再分配的。

我们知道,对象new出来之后都是放到堆的Eden区的,当Eden区放满之后,会进行minor gc,也就是由字节码执行引擎开启一个垃圾收集线程,收集Eden区的垃圾对象。minor gc是怎么处理垃圾的?需要先了解一个概念,gc root。

GC Roots

GC Roots根节点:线程栈的本地变量、静态变量、本地方法栈的变量等。

比如之前代码中,类名test,对象user等都可以作为GC Roots.

栈以及方法区都引用了堆中的对象,所以栈和方法区中的GC Roots都指向了堆中的部分对象,如果堆中被指向的对象还指向了别的对象,那么虚拟机会顺藤摸瓜找到呗指向的对象,但是如果栈和方法区中的不是GC Roots,那么就会清除掉这些垃圾。GC Roots 会被放到Survivor区的from区或者To区。这也是可达性分析算法的过程。

java静态类存储在那个空间 java静态数据存放在方法区_静态变量_05


分代年龄

minor gc将有用的对象分配到Survivor区的一个小分区,并将其分代年龄置为1,当再一次进行minor gc时,除了Eden区,Survivor区的对象也需要进行处理,如果还有用,那么就移到Survivor区的另一个小区,并将分代年龄+1。如果在minor gc期间,对象已经没有用,那么就当作垃圾清理掉。当Survivor区的对象的分代年龄达到15的时候,移到老年代