JVM内存结构和虚拟机栈

  • 起始
  • 简介
  • 内存结构
  • 虚拟机栈
  • 程序计数器是用来做什么的
  • 为什么会有native方法?
  • 为什么会有本地方法栈
  • 总结


起始

学会看官网 JDK1.8参数

简介

    java文件被编译为class文件,运行时,class文件被classLoader读取,大多数时候会被解释器解释为机器码,然后被操作系统执行,但是也会被JNI通过即时编译编译为机器码,最后被操作系统执行




大多数





JAVA源文件

class文件

classLoader

字节码解释器

JNI即时编译

机器码

操作系统


内存结构











内存结构

运行时数据区

堆外内存

线程私有

线程共享

虚拟机栈 -Xss

本地方法栈

程序计数器

堆 -Xmx -Xmx

方法区


JVM运行时数据区:

android 虚拟栈大小 java虚拟机栈大小_android 虚拟栈大小


图源:King老师

虚拟机栈

android 虚拟栈大小 java虚拟机栈大小_操作数_02


图源:King老师

方法调用时创建栈帧,并压入虚拟机栈中,当方法执行完毕时,栈帧从虚拟机栈中出栈。主要是做方法的出栈与入栈,同时,虚拟机栈是有大小限制的,可以通过-Xss来设置虚拟机栈的大小。在64位机器上,这个参数的默认值是1m,官方解释说明如下:

android 虚拟栈大小 java虚拟机栈大小_java_03

  • 栈溢出:一般是方法调用过深,常见于递归调用
  • 内存溢出:当线程创建过多时,每个线程会占用-Xss大小的内存,最后如果内存不足,也会OOM

理解:虚拟机栈中存放是栈针,方法被调用时,创建栈针并压入虚拟机栈中,栈帧分为四个组层部分,分别是局部变量表、操作数栈、动态连接和完成出口






栈帧

局部变量表

操作数栈

动态连接

完成出口


  • 局部变量表
    局部变量表中存储当前执行的方法中创建的8大基本数据类型,对自身的引用以及对堆中对象的引用
  • 操作数栈
    如果JVM的执行引擎相当于计算器的CPU的话,那么操作数栈相当于CPU的寄存器,操作数栈中存放了当前正在执行的指令以及结果(比如加法运算会先出栈再入栈)操作数栈中的命令会被JVM执行引擎执行

引申
如果读懂了字节码,就理解了一些优化的原则,比如

int i=0;
int y=10;
int z=(i+y)*100;
return z;

int i=0;
int y=10;
return (i+y)*100;

在效率上是不同的,因为int z=的时候,需要在操作数栈中先istore再iload最后再return,但是后面的方式节省了这两个指令

程序计数器是用来做什么的

因为CPU是采用时间划片的方式轮询各个任务的,在程序计数器中记录了当前CPU在当前线程当前方法中执行的字节码的地址

为什么会有native方法?

借鉴了C C++的一些类库,而且Java在初期也没有那么多的类库

为什么会有本地方法栈

虚拟机规范,但是HotSpot将本地方法栈和虚拟机栈合并了

总结

  1. 线程私有的区域有三个:本地方法栈、虚拟机栈、程序计数器,其中,程序计数器永远不会OOM,因为程序计数器只是保存了当前CPU执行到的方法的指令的地址。
  2. 虚拟机栈中存的是栈帧,每次方法调用都会产生栈帧并压入栈中。栈帧由四部分组成:动态连接,完成出口,操作数栈,局部变量表
    2.1 局部变量表:在局部变量表中,记录了当前方法中创建的8大基本类型的变量以及对象的引用
    2.2 操作数栈:操作数栈中存储的是当前CPU要执行的指令,相当于PC的寄存器