JVM-学习笔记

一、JVM体系结构

JVM的结构由4部分组成:
1、类加载器:JVM启动时将.class文件加载到JVM中;
2、执行引擎:执行.class文件的字节码指令,等同于机器上的CPU;
3、内存区(运行时数据区):等同于机器上各种功能的寄存器或PC指针的记录器等;
4、本地方法调用:调用C/C++实现本地方法的代码返回结果;

类加载器:ClassLoader
每个被JVM装载的类型都有一个对应的java.lang.Class累的实例来表示该类型,这个实例和其他类的实例一样都存放在Java的堆中;

执行引擎: 核心 部分
解析JVM字节码指令,得到执行结果;执行引擎就是执行一条条代码的流程,对应到操作系统中一个执行流程是一个Java 线程

内存区:Java内存管理
保存执行引擎在执行过程中,需要存储的一些东西。一个JVM实例会有一个方法区、Java堆、Java栈、PC寄存器和本地方法区。 方法区 Java堆 是所有线程共享的,可以被所有的执行引擎访问。创建新的执行引擎实例时,会为他创建一个 Java栈 PC寄存器 。PC寄存器指向即将执行的下一条指令。
本地方法调用,存储在本地方法调用栈中,或特定实现中的某个内存区域中。

二、JVM工作机制
一个程序从编写到执行会经历:
源代码→预处理器→编译器→汇编程序→目标代码→链接器→可执行程序

三、JVM基于栈的架构
所有的操作数都必须先入栈,然后根据指令中的操作码选择从 栈顶 弹出若干个元素进行计算后再将结果压入栈中。


四、JVM内存区域与内存溢出异常
1、运行时数据区(内存区):
(1)PC寄存器:
当前线程所执行的字节码的行号指示器。通过改变这个计数器的值选取下一条需要执行的字节码指令。
Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现,在任何确定的时刻,一个处理器(或者内核)只会执行一条线程的指令。为了保证线程切换之后能恢复到正确的执行位置,每个线程都需要一个独立PC寄存器。线程之间互不影响,独立存储,这类的内存区域为“线程私有”内存;
线程如果正在执行的是Java方法,计数器记录的是正在性的字节码指令的地址;如果正在执行Native方法(Java调用非java代码的接口),计数器值为空(Undefined)。
(2)Java虚拟机栈
与PC寄存器一样, Java栈也是线程私有的 ,生命周期与线程相同。Java栈描述的是Java方法执行的内存模型:方法在执行时会创建一个栈帧,用于存储 局部变量表、操作数栈、动态链接、方法出口 等信息。 一个方法的调用至完成,对应着一个栈帧在Java栈中入栈到出栈的过程 。最关注的是局部变量表。
局部变量表中存放了编译期间的基本数据类型(byte、char、short 、int、float、long、double),对象引用(不是对象本身,是指向对象起始地址的指针,总之就是位置),和returnAddress类型(指向一条字节码指令的地址)。
(3)本地方法栈 (Natice Method Stack)
与虚拟机栈发挥的作用非常相似,区别只是虚拟机栈为虚拟机执行java方法(也就是字节码)服务,本地方法栈为虚拟机使用到的Native方法服务。
(4)Java堆
Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。Java堆是 所有线程共享 的内存区域,在虚拟机启动时被创建。Java堆的唯一目的是 存放对象实例(new 出来的对象) ,几乎所有的对象实例都在这里分配内存。
Java堆是垃圾收集器管理的主要区域,所以也被叫做“GC堆”。所以从内存回收的角度看,Java堆细分为:新生代、老年代和持久代(HotSpot中的方法区称为持久代);再细分则是伊甸园(Eden)、幸存者空间。无论怎么分配空间,目的都是为了更好的回收内存,或更快的分配内存。
(5)方法区
方法区(Method Area)与Java堆一样,是各个线程共享的内存区域。用于存储已被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
(6)运行时常量池
属于方法区的一部分。Class文件中除了累的版本、字段、方法、接口等描述信息外,还有常量池,用于存放编译期生成的各种字面量和符号引用。

一般情况下理解的堆与栈:
栈:
1、函数中定义的变量(基本类型,int,short,long,byte,float,double,boolean,char)和对象的引用类型(引用变量)都保存在栈内存中;
2、定义一个变量后,会在栈中给变量分配一个内存空间。当超出这个变量的作用域之后会释放这个内存供其它使用;
3、优点:栈的存取速度比堆要快,仅次于寄存器,数据可共享;
4、缺点:栈中的数据大小与生存期必须是明确的,所以缺乏灵活性;
堆:运行时数据区
1、new出来的对象(数组等)存在堆内存中;
2、JVM的自动垃圾回收器管理堆内存;
3、可以在栈中定义一个变量,指向堆中的对象或数组(首地址),此时栈中的这个变量就成为了引用变量;

变量在内存中的分配:
1、类变量(static修饰的变量):加载程序时在堆中开辟了内存,栈中存放了static变量在堆中的地址以便高速访问。系统关闭时才释放static变量。
2、局部变量:在某个方法或某个代码片段(for循环)中的变量,执行到他的时候会在栈中开辟内存,执行完毕(脱离工作域)后,立即释放内存。
编译器处理变量时,会将编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向3。接着处理int b = 3;在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就出现了a与b同时均指向3的情况。这时,如果再令a=4;那么编译器会重新搜索栈中是否有4值,如果没有,则将4存放进来,并令a指向4;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。要注意这种数据的共享与两个对象的引用同时指向一个对象的这种共享是不同的,因为这种情况a的修改并不会影响到b, 它是由编译器完成的,它有利于节省空间。而一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量 



你可能感兴趣的:(学习笔记)