Skip to the content.

从一段代码来看Java的内存管理

代码比较简单,就是new一个小狗,给它取个名字,叫一下。

public class test {

    public static void main(String[] args) {
        Dog yellow = new Dog("大黄");
        yellow.say();
        Dog white = new Dog("小白");
        white.say();
    }
}

public class Dog {
    private String name;

    public Dog(String name) {
        this.name = name;
    }

    public void say() {
        System.out.println(name + "叫 汪汪");
    }
}

Dog yellow = new Dog("大黄");

Dog white = new Dog("小白");

不难理解,这里new出来的实例对象是需要存储起来的,那这个实例存在哪了呢?new出来的对象实例都存放到了一块内存上,这块内存叫堆(Heap)。

image.png

至于Dog这个类,Dog这个类有自己的属性name,有自己的方法say。这些信息,被称为元数据,同样有地方存储。存储的地方称为方法区,在JDK1.8又称为元数据空间。

image.png

(yellow)大黄和(white)小白,这两个对象实例怎么知道自己属于哪个类呢?其实对象实例中会存在一个类型指针指向它的类型。用来知道(yellow)大黄属于Dog类的,知道(white)小白属于Dog类的。 ​

image.png

在java中会有很多类,也会有很多对象实例,类信息都存在了方法区/元数据空间中,new出来的对象实例都存在了堆Heap中。 ​

在java中,会有很多线程一起工作。所有线程使用到的类,new出来的对象实例,都分别存放在了方法区/元数据空间、堆Heap中。所以堆Heap和元数据空间是所有线程共享的,共同拥有的。 ​

刚才讲了,java中会有很多线程,每个线程都会执行函数,函数的执行都是在栈Stack中进行的。还是最上面的例子yellow.say(); 这些函数间的相互调用,都存放到栈中了,栈也是需要内存来存储的。 ​

image.png

栈帧中就存储了变量、方法出口等信息。

image.png

栈帧中存储了对象指针,也就是Heap堆中的对象实例大黄的地址。

image.png

在从内存的情况来看 ​

栈内存:每个线程一块内存,单独的。 堆内存:多个线程共享内存,共享的,共用的。 元数据空间内存:多个线程共享内存,共享的,共用的。

image.png

栈内存分为两类:一类是虚拟机栈(VM Stack),一类是本地方法栈(Native Stack)

另外单线程中还有一块区域存的是程序计数器,用于多线程切换保留当时执行的位置。

先来说说栈内存 如何设置栈内存大小呢?

如果java程序运行期间超出了设置的栈内存大小呢? 代码如下。

public class Test {

    public static void main(String[] args) {
        leak();
    }

    public static void leak(){
        while (true) {
            leak();
        }
    }
}

// 报错如下
Exception in thread "main" java.lang.StackOverflowError
	......

再来说下堆Heap内存 如何设置堆内存大小呢?

一般情况下,设置-Xms的值等于-Xmx的值 ​

最后来说下方法区、元数据空间内存 如何设置元数据空间大小呢? 在JDK1.8中使用

一般情况下,设置-XX:MetaspaceSize的值等于-XX:MaxMetaspaceSize的值 ​

留个思考题 多个线程都会操作堆Heap区,多个线程在同一个Heap中申请内存,是如何保障多个线程相互不影响的呢?多个线程同时操作Heap,效率是如何保证的呢?

在这里评论回复即可