原创

JVM内存结构


正式开始之前,有必要先复习一下几个java比较基础的题。

1. 关于byte类型取值范围(-128 到127)的推导

按照网上和教科书上的大部分解释是这样: 最大的正数原码是:0111 1111 正数的原码补码相同,补码也是:0111 1111 这个无疑问,转为10进制是127。但是负数怎么表示呢,按原反补码来说,就是1111 1111,1000 0000,1000 0010,转为10进制就是2, 怎么会是-128呢? 如果要表示128,那就是1000 0000 但是按照书上的说法,首位是符号位,后面干脆来了一个+0和-0的概念,这怎么解释?难道-128首位既是符号位又是数值位? 后面我是这么理解的:1byte=8bit,一个byte8位,也就是能表示2的8次方,256个数,然后正数0到127一共128个,负数就是-128到-1 共128个,那么他的范围就是-128 到127。

2. 关于char类型和ASCII码

这个也是坑了我好久,一段时间我都是很迷糊的。A:65,a:97.直接上图 char类型易错点


3. JVM内存结构初识:以HotSpot为例(正文来了)

之前我一度分不清JVM内存模型(Java Virtual Machine)和Java内存模型(Java Memory Model) 甚至怀疑这两个东西是不是同一个东西。实际上JVM和JMM还是有许多不同之处的。

  • 下面2张图可以看到比较清晰的JVM的内存结构 JVM内存结构

注:下图是JDK7版本,JDK8有少许变化 JVM内存结构

可以看出:JVM内存结构大致由4个部分构成

  1. Classloader(类加载器)子系统 根据给定的全限定名类名(如java.lang.Object)来装载class文件的内容到Runtimedataarea中的methodarea(方法区域)。我们可以extends java.lang.ClassLoader类来写自己的Classloader。

  2. Executionengine(执行引擎)子系统 执行JVM下达的Class中的指令。任何JVM specification(规范)实现(JDK)的核心都是Executionengine。

  3. Nativeinterface(本地接口)组件 Java Native Interface,与操作系统交互,是其它编程语言交互的接口(例如动态调用dll库)。当调用native方法的时候,就进入不再受虚拟机限制。

  4. RuntimeDataArea(运行时数据区)组件 我们俗称的JVM的内存区域(通常意义上的JVM的内存区域)

4. 其中JVM的运行时数据区大概有几个主要部分

  • 程序计数器(Program Count Register) 也叫PC寄存器,简单理解就是JVM解释执行Class时用来执行下一行的,可以通过改变计数器的值改变当前执行的行数。计数器是线程私有的,每个线程都有独立的计数器,互不影响。如果线程正在执行的是Java方法,那么该计数器记录的是正在执行Class字节码指令的地址(可以理解为行号),如果线程执行的是Native(本地)方法,该计数器的值为空。

  • Java栈(Java Virtual Machines Stacks) 描述java方法执行的内存模型。内存很小,和计数器一样是线程私有的,用于存储局部变量表,操作数栈等等。具体有 基本数据类型(byte,short,int,long,float,double,char,boolean)数据以及引用(reference)类型的内存地址指针或句柄。每个方法被执行的时候都会创建一个"栈帧"用于存储局部变量表(包括参数)、操作栈、方法出口等信息,对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。可以通过参数:-Xss调节栈的内存大小。 如果栈内存过小,会抛出StackOverflowError异常。

  • 本地方法栈(Native Method Stacks) 和JVM栈类似,只不过是为Native方法服务。例如与当前的操作系统交互,调用其他语言接口。

  • Java堆(Java Heap) Java堆是JVM内存模型中最大的一个部分,它是一个被所有线程共享的一块区域,在虚拟机启动时创建。存放对象实例以及数组,几乎所有的对象实例在这里分配内存。Java堆是GC管理的区域,有时候又叫"GC堆"。由于现在GC收集器都采用分代算法,所以Java堆还可以细分为:新生代和老年代。Java堆的大小可以通过-Xms(最小值)和-Xmx(最大值)参数设置,-Xms为JVM启动时申请的最小内存,默认为操作系统物理内存的1/64,-Xmx为JVM可申请的最大内存,默认为物理内存的1/4,默认当空余堆内存小于40%时,JVM会增大Java Heap直到-Xmx指定值的最大限制,可通过-xx:MinHeapFreeRation=值,来指定这个比例的大小;当空余堆内存大于70%时,JVM会减小heap直到-Xms指定值的最小限制,可通过-xx:MaxHeapFreeRation=值,来指定这个比例的大小。

  • 方法区(Method Area) 注:java8中方法区实际是元空间.也称为"永久代"、"非堆"。它用于存储虚拟机加载的所有的①类(class),②静态变量(static变量),③静态方法,④常量和⑤成员方法。也是各个线程共享的内存区域。默认最小值为16MB,最大值为64MB,可以通过**-xx:PermSize和-xx:MaxPermSize参数调节方法区的大小,当方法区内存过小时,将抛出:OutOfMemoryError** 异常。注:在JDK7中,String等常量信息不再置于方法区,已经移到了Java堆中。

  • 运行时常量池(Runtime Constant Pool) **是方法区中的一部分,该区域除了存放类和接口的常量,它还存放成员变量和成员方法的所有引用。**当一个成员变量或者成员方法被引用的时候,JVM就通过运行常量池中的这些引用来查找成员变量和成员方法在内存中的的实际地址。当常量池无法申请到内存导致内存过小时,将抛出:OutOfMemoryError 异常。


5. JVM栈,堆,方法区交互简图

JVM栈,堆,方法区交互


简单来说就是: JVM运行时会把类的信息加载到方法区,main方法本身也放入方法区,对象的引用变量放到栈里,new出来的对象放在堆区。

JVM
java
  • 作者:管理员(联系作者)
  • 发表时间:2020-03-19 10:13
  • 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
  • 公众号转载:请在文末添加作者公众号二维码
  • 微信公众号

    评论

    留言