JVM系列(一):深刻详解JVM 内存区域总结!

前言

在JVM的管控下,Java程序员再也不须要管理内存的分配与释放,这和在C和C++的世界是彻底不同的。因此,在JVM的帮助下,Java程序员不多会关注内存泄露和内存溢出的问题。可是,一旦JVM发生这些状况的时候,若是你不清楚JVM内存的内存管理机制是很难定位与解决问题的。程序员

1、JVM 内存区域

Java虚拟机在运行时,会把内存空间分为若干个区域,根据《Java虚拟机规范(Java SE 7 版)》的规定,Java虚拟机所管理的内存区域分为以下部分:方法区、堆内存、虚拟机栈、本地方法栈、程序计数器。算法

一、方法区

方法区主要用于存储虚拟机加载的类信息、常量、静态变量,以及编译器编译后的代码等数据。在jdk1.7及其以前,方法区是堆的一个“逻辑部分”(一片连续的堆空间),但为了与堆作区分,方法区还有个名字叫“非堆”,也有人用“永久代”(HotSpot对方法区的实现方法)来表示方法区。数组

从jdk1.7已经开始准备“去永久代”的规划,jdk1.7的HotSpot中,已经把本来放在方法区中的静态变量、字符串常量池等移到堆内存中,(常量池除字符串常量池还有class常量池等),这里只是把字符串常量池移到堆内存中;在jdk1.8中,方法区已经不存在,原方法区中存储的类信息、编译后的代码数据等已经移动到了元空间(MetaSpace)中,元空间并无处于堆内存上,而是直接占用的本地内存(NativeMemory)。根据网上的资料结合本身的理解对jdk1.3~1.六、jdk1.七、jdk1.8中方法区的变迁画了张图以下(若有不合理的地方但愿读者指出):性能

去永久代的缘由有:spa

  1. 字符串存在永久代中,容易出现性能问题和内存溢出。线程

  2. 类及方法的信息等比较难肯定其大小,所以对于永久代的大小指定比较困难,过小容易出现永久代溢出,太大则容易致使老年代溢出。3d

  3. 永久代会为 GC 带来没必要要的复杂度,而且回收效率偏低。cdn

二、堆内存

堆内存主要用于存放对象和数组,它是JVM管理的内存中最大的一块区域,堆内存和方法区都被全部线程共享,在虚拟机启动时建立。在垃圾收集的层面上来看,因为如今收集器基本上都采用分代收集算法,所以堆还能够分为新生代(YoungGeneration)和老年代(OldGeneration),新生代还能够分为 Eden、From Survivor、To Survivor。对象

三、程序计数器

程序计数器是一块很是小的内存空间,能够看作是当前线程执行字节码的行号指示器,每一个线程都有一个独立的程序计数器,所以程序计数器是线程私有的一块空间,此外,程序计数器是Java虚拟机规定的惟一不会发生内存溢出的区域。blog

四、虚拟机栈

虚拟机栈也是每一个线程私有的一块内存空间,它描述的是方法的内存模型,直接看下图所示:

虚拟机会为每一个线程分配一个虚拟机栈,每一个虚拟机栈中都有若干个栈帧,每一个栈帧中存储了局部变量表、操做数栈、动态连接、返回地址等。一个栈帧就对应 Java 代码中的一个方法,当线程执行到一个方法时,就表明这个方法对应的栈帧已经进入虚拟机栈而且处于栈顶的位置,每个 Java 方法从被调用到执行结束,就对应了一个栈帧从入栈到出栈的过程。

五、本地方法栈

本地方法栈与虚拟机栈的区别是,虚拟机栈执行的是 Java 方法,本地方法栈执行的是本地方法(Native Method),其余基本上一致,在 HotSpot 中直接把本地方法栈和虚拟机栈合二为一,这里暂时不作过多叙述。

六、元空间

上面说到,jdk1.8 中,已经不存在永久代(方法区),替代它的一块空间叫作 “ 元空间 ”,和永久代相似,都是 JVM 规范对方法区的实现,可是元空间并不在虚拟机中,而是使用本地内存,元空间的大小仅受本地内存限制,但能够经过 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 来指定元空间的大小。

总结

JVM内存区域划分,便于它可以更加高效的管理自身的内存。当程序中出现这种因为JVM形成的内存溢出的状况的时候,须要根据不一样的状况作不一样的分析与处理。

相关文章
相关标签/搜索