聊聊JVM内存结构

99 阅读3分钟

一、简介

java程序是由JVM(Java 虚拟机)执行的,在执行过程中将内存划分为多个不同的区域

image.png

上图所示,首先,JVM不认识文本文件,由Java编译器编译后变成二进制.class文件,然后由JVM中的类加载器记载到JVM中,JVM进行对程序的内存管理。

二、JVM内存管理

JVM的内存结构也就是JVM的运行时数据区域

根据JVM《Java 虚拟机规范》的规定,JVM将内存划分成如下几个区域

1、程序计数器(Program Counter Register2Java虚拟机栈(Java Virtual Machine Stack3、本地方法栈(Native Method Stacks4Java堆(Java Heap5、方法区(Method Area

20200608150440.png

2.1 程序计数器

作用:用于保存JVM中下条指令的地址

Java虚拟机的多线程是通过线程的切换实现的,每个线程都有独立的程序计数器。为了线程切换后能恢复到正确的执行位置,此时我么就需要一块区域来保存当前线程的执行信息

特点

  • 线程私有

    • CPU会为每个线程分配时间片,当当前线程的时间片使用完以后,CPU就会去执行另一个线程中的代码
    • 程序计数器是每个线程私有的,当另一个线程的时间片用完,又返回来执行当前线程的代码时,通过程序计数器可以知道应该执行哪一句指令
  • 不会存在内存溢出

JVM指令的运行过程:

image (2).png

2.2 Java虚拟机栈

每个线程被创建的时候,都会创建一个虚拟机栈,每次方法被调用时,都会创建一个栈帧。栈帧中包含方法参数、局部变量、返回地址等,而每个线程只能有一个活动栈帧,对应着当前正在执行的方法

栈的结构:

image (3).png

演示:

public class Main {
    public static void main(String[] args) {
        method1();
    }

    private static void method1() {
        method2(1, 2);
    }

    private static int method2(int a, int b) {
        int c = a + b;
        return c;
    }
}

20200608150534.png

2.3 本地方法栈

一些带有native关键字的方法就是需要JAVA去调用本地的C或者C++方法,因为JAVA有时候没法直接和操作系统底层交互,所以需要用到本地方法

2.4 Java堆

堆区主要用于存放对象实例及数组,通过new关键字创建出的对象都会被放在堆内存中

特点

  • 所有线程共享,堆内存中的对象都需要考虑线程安全问题
  • 有垃圾回收机制

2.5 方法区

  1. 方法区用于存储虚拟机加载的类信息(类的版本、字段、方法、接口),常量,静态变量,即时编译器编译后的代码等数据。如果方法区无法满足新的内存分配需求时,将抛出OutOfMeMoryError异常。
  2. PerGen(永久代):绝大部分的程序员都应该见过"java.lang.OutOfMemoryError:PreGen space"异常,这里的PermGen space其实就是指的是方法区,但是上面我们已经说了方法区和永久代有着本质的区别,前者是JVM的规范,而后者则是JVM规范的一种实现,只有HotSpot才有PermGen space,由于方法区主要存储类的相关信息,所以对于动态生成类的情况比较容易出现永久代的内存溢出。 JDK1.7以前使用永久代来实现方法区
  3. 元空间:
    其实,移除永久代的工作从JDK1.7就开始了,JDK1.7中,在存储永久代的部分数据就已经转移到Java Heap 或者Native Heap,但永久代仍存在于JDK1.7中,并没有完全移除,比如符号引用(Symbols)转移到了Native Heap,字面量(interned strings)转移到Java Heap,类的静态变量(class statics)转移到了Java Heap。元空间存储不在虚拟机中,而是使用本地内存,JVM 不会再出现方法区的内存溢出

20200608150547.png