很多人会误以为Java内存区域和内存模型是同一个货色,其实并不是。

Java内存区域是指 JVM运行时将数据分区域存储 ,简略的说就是不同的数据放在不同的中央。通常又叫 运行时数据区域

Java内存模型(JMM)定义了程序中各个变量的拜访规定,即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节。

1、Java内存区域

1.8 之前:
JDK1.8(含)之后:

区别就是 1.8有一个元数据区代替办法区了。

JDK 1.7 其实是并没齐全移除办法区,然而不会像1.6以前报 “java.lang.OutOfMemoryError: PermGen space”,而是报 java.lang.OutOfMemoryError: Java heap space

1.7局部内容(比方 常量池、动态变量有办法区转移到了堆)

那么,Java 8 中 PermGen 为什么被移出 HotSpot JVM 了?我总结了两个次要起因(详见:JEP 122: Remove the Permanent Generation):

  1. 因为 PermGen 内存常常会溢出,引发宜人的 java.lang.OutOfMemoryError: PermGen,因而 JVM 的开发者心愿这一块内存能够更灵便地被治理,不要再经常出现这样的 OOM
  2. 移除 PermGen 能够促成 HotSpot JVM 与 JRockit VM 的交融,因为 JRockit 没有永恒代。

依据下面的各种起因,PermGen 最终被移除,办法区移至 Metaspace,字符串常量移至 Java Heap

援用自https://www.sczyh30.com/posts...

上面逐个介绍一下jvm管辖的这几种内存区域。

2、程序计数器

程序计数器(Program Counter Register)是一块较小的内存空间,因为JVM能够并发执行线程,因而会存在线程之间的切换,而这个时候就程序计数器会记录下以后程序执行到的地位,以便在其余线程执行结束后,复原现场继续执行。

JVM会为每个线程调配一个程序计数器,与线程的生命周期雷同。

如果线程正在执行的是应该Java办法,这个计数器记录的是正在执行虚拟机字节码指令的地址。

如果正在执行的是Native办法,计数器的值则为空(undefined)

程序计数器是惟一一个在 Java 虚拟机标准中没有规定任何 OutOfMemoryError 状况的区域。

3、Java虚拟机栈

虚拟机栈 形容的是 Java 办法执行的内存模型:

每个办法在执行的同时都会创立一个栈帧(Stack Frame,是办法运行时的根底数据结构)用于存储局部变量表、操作数栈、动静链接、办法进口等信息。每一个办法从调用直至执行实现的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

虚拟机栈是每个线程独有的,随着线程的创立而存在,线程完结而死亡。

在虚拟机栈内存不够的时候会OutOfMemoryError,在线程运行中须要更大的虚拟机栈时会呈现StackOverFlowError

虚拟机栈蕴含很多栈帧,每个办法执行的同时会创立一个栈帧,栈帧又存储了办法的局部变量表、操作数栈、动静连贯和办法返回地址等信息。

在流动线程中,只有位于栈顶的栈帧才是无效的,称为以后栈帧,与这个栈帧相关联的办法称为以后办法。
1)局部变量表

局部变量表是寄存办法参数局部变量的区域。

全局变量是放在堆的,有两次赋值的阶段,一次在类加载的筹备阶段,赋予零碎初始值;另外一次在类加载的初始化阶段,赋予代码定义的初始值。

而局部变量没有赋初始值是不能应用的。

2)操作数栈

一个先入后出的栈。

当一个办法刚刚开始执行的时候,这个办法的操作数栈是空的,在办法的执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,也就是出栈/入栈操作。

3) 动静连贯

 每个栈帧都蕴含一个指向运行时常量池中该栈帧所属办法的援用。持有这个援用是为了反对办法调用过程中的动静连贯(Dynamic Linking)。

常量池能够便于指令的辨认
    public void methodA(){            }    public void methodB(){        methodA();//methodB()调用methodA(),先找到调用methodA()的版本符号,再变为间接援用    }

办法调用并不等同于办法执行,办法调用阶段惟一的工作就是确定被调用办法的版本(即调用哪一个办法),这也是Java弱小的扩大能力,在运行期间能力确定指标办法的间接援用

所有办法调用中的指标办法在Class文件外面都是一个常量池中的符号援用,在类加载的解析阶段,会将其中的一部分符号援用转化为间接援用。

4)办法返回地址(办法进口)

返回分为 失常返回 和 异样退出。

无论何种退出状况,都将返回至办法以后被调用的地位,这也程序能力继续执行。

一般来说,办法失常退出时,调用者的PC计数器的值能够作为返回地址,栈帧中会保留这个计数器值。

办法退出的过程相当于弹出以后栈帧。

4、本地办法栈

Java虚拟机栈是调用Java办法;本地办法栈是调用本地native办法,能够认为是通过 JNI (Java Native Interface) 间接调用本地 C/C++ 库,不受JVM管制。

本地办法栈也会抛出 StackOverflowErrorOutOfMemoryError 异样

5、Java堆

Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创立。此内存区域的惟一目标就是寄存对象实例,简直所有的对象实例都在这里分配内存。

堆是垃圾收集器治理的次要区域,又称为“GC堆”,能够说是Java虚拟机治理的内存中最大的一块。

当初的虚拟机(包含HotSpot VM)都是采纳分代回收算法。在分代回收的思维中, 把堆分为:新生代+老年代+永恒代(1.8没有了); 新生代 又分为 Eden + From Survivor + To Survivor区。

6、办法区

办法区(Method Area)与 Java 堆一样,是所有线程共享的内存区域。

办法区用于存储曾经被虚拟机加载的类信息(即加载类时须要加载的信息,包含版本、field、办法、接口等信息)、final常量、动态变量、编译器即时编译的代码等。

办法区逻辑上属于堆的一部分,然而为了与堆进行辨别,通常又叫“非堆”。

办法区比拟重要的一部分是运行时常量池(Runtime Constant Pool),为什么叫运行时常量池呢?是因为运行期间可能会把新的常量放入池中,比如说常见的String的intern()办法。

String a = "I am HaC";Integer b = 100;

在编译阶段就把所有的字符串文字放到一个常量池中,复用同一个(比如说上述的“I am HaC”),节俭空间。

对于办法区和元空间的关系:

办法区是JVM标准概念,而永恒代则是Hotspot虚拟机特有的概念,简略点了解:办法区和堆内存的永恒代其实一个货色,然而办法区是蕴含了永恒代。

只有 HotSpot 才有 “PermGen space”,而对于其余类型的虚拟机,如 JRockit(Oracle)、J9(IBM) 并没有“PermGen space”

7、元空间

1.8就把办法区改用元空间了。类的元信息被存储在元空间中。元空间没有应用堆内存,而是与堆不相连的本地内存区域。所以,实践上零碎能够应用的内存有多大,元空间就有多大,所以不会呈现永恒代存在时的内存溢出问题。

能够通过 -XX:MetaspaceSize-XX:MaxMetaspaceSize 来指定元空间的大小。

8、总结:

参考:

  • https://www.cnblogs.com/czwbi...
  • https://blog.csdn.net/xyh9309...
  • https://www.cnblogs.com/paddi...
  • 《深刻了解Java虚拟机》
  • 《码出高效》