复习Java虚拟机:JVM中的Stack和Heap

在JVM中,内存分为两个部分,Stack(栈)和Heap(堆)。这里,我们从JVM的内存管理原理的角度来认识Stack和Heap,并通过这些原理认清Java中静态方法和静态属性的问题。

一般,JVM的内存分为两部分:Stack和Heap。

Stack(栈)是JVM的内存指令区。Stack管理非常easy,push一定长度字节的数据或者指令。Stack指针压栈相应的字节位移;pop一定字节长度数据或者指令,Stack指针弹栈。Stack的速度非常快,管理非常easy,而且每次操作的数据或者指令字节长度是已知的。

所以Java 基本数据类型。Java 指令代码,常量都保存在Stack中。

Heap(堆)是JVM的内存数据区。

Heap 的管理非常复杂。每次分配不定长的内存空间,专门用来保存对象的实例。

在Heap 中分配一定的内存来保存对象实例,实际上也仅仅是保存对象实例的属性值。属性的类型和对象本身的类型标记等,并不保存对象的方法(方法是指令,保存在Stack中),在Heap 中分配一定的内存保存对象实例和对象的序列化比較相似。

而对象实例在Heap 中分配好以后,须要在Stack中保存一个4字节的Heap 内存地址,用来定位该对象实例在Heap 中的位置,便于找到该对象实例。

由于Stack的内存管理是顺序分配的,而且定长。不存在内存回收问题;而Heap 则是随机分配内存,不定长度,存在内存分配和回收的问题;因此在JVM中另有一个GC进程,定期扫描Heap ,它依据Stack中保存的4字节对象地址扫描Heap 。定位Heap 中这些对象。进行一些优化(比如合并空暇内存块什么的),而且假设Heap 中没有扫描到的区域都是空暇的,统统refresh(实际上是把Stack中丢失了对象地址的无用对象清除了),这就是垃圾收集的过程;关于垃圾收集的更深入解说请參考51CTO之前的文章《JVM内存模型及垃圾收集策略解析》。

JVM的体系结构 
JVM的体系结构

我们首先要搞清楚的是什么是数据以及什么是指令。然后要搞清楚对象的方法和对象的属性分别保存在哪里。

1)方法本身是指令的操作码部分,保存在Stack中;

2)方法内部变量作为指令的操作数部分,跟在指令的操作码之后,保存在Stack中(实际上是简单类型保存在Stack中,对象类型在Stack中保存地址,在Heap 中保存值);上述的指令操作码和指令操作数构成了完整的Java 指令。

3)对象实例包含其属性值作为数据。保存在数据区Heap 中。

非静态的对象属性作为对象实例的一部分保存在Heap 中,而对象实例必须通过Stack中保存的地址指针才干訪问到。因此是否能訪问到对象实例以及它的非静态属性值全然取决于是否能获得对象实例在Stack中的地址指针。

非静态方法和静态方法的差别:

非静态方法有一个和静态方法非常重大的不同:非静态方法有一个隐含的传入參数,该參数是JVM给它的,和我们怎么写代码无关,这个隐含的參数就是对象实例在Stack中的地址指针。

因此非静态方法(在Stack中的指令代码)总是能够找到自己的专用数据(在Heap 中的对象属性值)。当然非静态方法也必须获得该隐含參数,因此非静态方法在调用前,必须先new一个对象实例,获得Stack中的地址指针。否则JVM将无法将隐含參数传给非静态方法。

静态方法无此隐含參数,因此也不须要new对象,仅仅要class文件被ClassLoader load进入JVM的Stack,该静态方法就可以被调用。

当然此时静态方法是存取不到Heap 中的对象属性的。

总结一下该过程:当一个class文件被ClassLoader load进入JVM后,方法指令保存在Stack中,此时Heap 区没有数据。然后程序技术器開始执行指令。假设是静态方法,直接依次执行指令代码,当然此时指令代码是不能訪问Heap 数据区的;假设是非静态方法。由于隐含參数没有值,会报错。因此在非静态方法执行前,要先new对象。在Heap 中分配数据,并把Stack中的地址指针交给非静态方法,这样程序技术器依次执行指令,而指令代码此时能够訪问到Heap 数据区了。

静态属性和动态属性

前面提到对象实例以及动态属性都是保存在Heap 中的,而Heap 必须通过Stack中的地址指针才干够被指令(类的方法)訪问到。因此能够判断出:静态属性是保存在Stack中的,而不同于动态属性保存在Heap 中。正由于都是在Stack中。而Stack中指令和数据都是定长的,因此非常easy算出偏移量,也因此无论什么指令(类的方法),都能够訪问到类的静态属性。也正由于静态属性被保存在Stack中,所以具有了全局属性。

在JVM中,静态属性保存在Stack指令内存区,动态属性保存在Heap数据内存区。



-------------------------------

二、Java虚拟机体系结构

Java虚拟机由五个部分组成:一组指令集、一组寄存器、一个栈、一个无用单元收集堆(Garbage-collected-heap)、一个方法区域。这五部分是Java虚拟机的逻辑成份,不依赖不论什么实现技术或组织方式,但它们的功能必须在真实机器上以某种方式实现。

Java语言写的源程序通过Java编译器,编译成与平台无关的‘字节码程序’(.class文件,也就是0,1二进制程序)。然后在OS之上的Java解释器中解释执行,而JVM是java的核心和基础,在java编译器和os平台之间的虚拟处理器

JAVA和JVM执行的原理

1.Java语言执行的过程

Java语言写的源程序通过Java编译器,编译成与平台无关的‘字节码程序’(.class文件,也就是0。1二进制程序),然后在OS之上的Java解释器中解释执行。

Java语言执行的过程

也相当与

Java语言执行的过程

注:JVM(java虚拟机)包含解释器,不同的JDK虚拟机是同样的,解释器不同。

2.JVM:

JVM是java的核心和基础,在java编译器和os平台之间的虚拟处理器。它是一种利用软件方法实现的抽象的计算机基于下层的操作系统和硬件平台。能够在上面执行java的字节码程序。

java编译器仅仅要面向JVM,生成JVM能理解的代码或字节码文件。

Java源文件经编译成字节码程序,通过JVM将每一条指令翻译成不同平台机器码。通过特定平台执行。

JVM执行程序的过程 :

I.载入。

class文件

II.管理并分配内存

III.执行垃圾收集

JRE(java执行时环境)由JVM构造的java程序的执行环境 

JVM执行程序的过程





JVM 原理解释

  JVM 全称是 Java Virtual Machine ,Java 虚拟机。这个 JVM 你是看不到的,它存在内存中。

我们知道计算机的基本构成是:运算器、控制器、存储器、输入和输出设备。那这个 JVM 也是有这成套的元素,运算器是当然是交给硬件 CPU 还处理了。仅仅是为了适应“一次编译。随处执行”的情况,须要做一个翻译动作,于是就用了JVM 自己的命令集,JVM 的命令集则是能够到处执行的。由于 JVM 做了翻译,依据不同的CPU ,翻译成不同的机器语言。

  JVM 是一个内存中的虚拟机,那它的存储就是内存了,我们写的全部类、常量、变量、方法都在内存中。

  JVM 的组成部分


  Class Loader 类载入器

  类载入器的作用是载入类文件(.class)到内存。Class Loader 载入的 class 文件是有格式要求的。

  类载入的终于产品是位于执行时数据区的堆区的Class对象。

  Class对象封装了类在方法区内部的数据结构。

 

  而且向JAVA程序提供了訪问类在方法区内的数据结构。

  JVM载入class文件的原理机制

  1. Java 中的全部类,必须被装载到 JMV 中才干执行。这个装载工作是由 JVM 中的类装载器完毕的,类装载器所做的工作实质是把类文件从硬盘读取到内存中。

  2. Java中的类大致分为三种:

  a) 系统类

  b) 扩展类

  c) 由程序猿自己定义的类

  3. 类装载方式。有两种:

  a) 隐式装载。程序在执行过程中当碰到通过 new 等方式生成对象时,隐式调用类装载器载入相应的类到jvm中。

  b) 显式装载,通过 class.forname() 等方法。显式载入须要的类。  

  4. 类载入的动态性体现

  一个应用程序总是由n多个类组成,Java 程序启动时,并非一次把全部的类全部载入后再执行。它总是先把保证程序执行的基础类一次性载入到 JVM 中,其他类等到 JVM 用到的时候再载入,这种优点是节省了内存的开销。

由于java最早就是为嵌入式系统而设计的。内存宝贵,这是一种能够理解的机制,而用到时再载入这也是 Java 动态性的一种体现。

  5.Java 类装载器

  Java 中的类装载器实质上也是类。功能是把类载入 JVM 中。值得注意的是 JVM 的类装载器并非一个,而是三个,层次结构例如以下:

      Bootstrap Loader  - 负责载入系统类 
            | 
          - - ExtClassLoader  - 负责载入扩展类 
                          | 
                      - - AppClassLoader  - 负责载入应用类

  为什么要有三个类载入器,一方面是分工。各自负责各自的区块,还有一方面为了实现托付模型。

  6. 类载入器之间是怎样协调工作的

  前面说了,Java中有三个类载入器。问题就来了,碰到一个类须要载入时,它们之间是怎样协调工作的,即 Java 是怎样区分一个类该由哪个类载入器来完毕呢。

  在这里Java採用了托付模型机制。这个机制简单来讲。就是“类装载器有载入类的需求时,会先请示其Parent使用其搜索路径帮忙载入,假设 Parent 找不到,那么才由自己按照自己的搜索路径搜索类”,注意喔,这句话具有递归性。


posted @ 2017-05-12 10:39  wzzkaifa  阅读(301)  评论(0编辑  收藏  举报