深入理解JVM 学习笔记2

Java内存区域

在执行java程序的过程中JVM会把它管理的内存划分为多个不同的数据区域。 根据《Java 虚拟机规范 SE7版》的规定,Java 虚拟机所管理的内存将会包括以下几个运行时数据区域. 

 程序计数器  

      较小的一块内存区域,可以看作是当前线程所执行的字节码的行号指示器,在我们运行java程序的时,jvm 通过它来获取下一步的执行逻辑 也就是字节码解释器。 字节码解释器通过update这个计数器的值来选取下一条需要执行的字节码指令。 想象一下我们多线程执行的代码,每个线程执行的代码步骤 或者 说当前线程需要执行的下一步指令,jvm如何知道? 我们都知道多线程中一般都是有一个Main Thread 和 More sub Thread  大多数线程都是这样,从Main Thread 来启动一些sub Thread 。 每个线程因为获取CPU的时钟的不确定性,执行多久等等因数,jvm通过维护线程自身的程序计数器获取每个指令,每个线程都有自己的私有内存区域和计数器,从而jvm通过它来获取next。 

虚拟机栈 

     虚拟机栈描述的是Java方法执行的内存模型: 每个方法在执行的时候都会创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每一个方法从调用知道执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。 局部变量表,是用来存入编译期可知的各种基础数据类型、对象的引用类型和returnAddress(指向了一条字节码指令的地址)。 局部变量表的内存空间在编译期间完成分配,在方法运行期间不会改变局部变量表的大小,因为在编译期间每个类型的数据大小是固定的,除了long 和 double类型占用两个局部变量空间外,其他都只是一个空间。

本地方法栈

    他与虚拟机栈的作用非常相似,他们之间的区别,本地方法栈为虚拟机使用到了Native方法服务,虚拟机栈是为java程序的方法服务。因为本地方法栈中使用的语言,使用的方式与数据结构并没有强制规定,因为每种虚拟机可以自由的实现它。其实有一个疑问,多线程中如果有调用Native方法,native方法是否是线程安全的,如果是线程安全的,那么jvm是在线程自己的内存区域有一个本地方法栈吗? 如果不是线程安全,那么他们直接每次都直接调用主线程的本地方法栈,是否需要而外的同步呢?待请教过大神后来解释....(大神也不是很确定,但是他也说了,一般我们线程问题都是因为存在访问共享内存,native方法你完全不知道他的具体实现,又怎么可以获取到他的变量信息呢? )

转载自 http://hi.baidu.com/holder/item/e71e50a87389da9f151073e0, 以下是其中一段:

对于一个运行中的Java程序而言,它还可能会用到一些跟本地方法相关的数据区。当某个线程调用一个本地方法时,它就进入了一个全新的并且不再受虚拟机限制的世界。本地方法可以通过本地方法接口来访问虚拟机的运行时数据区,但不止于此,它还可以做任何它想做的事情。比如它甚至可以直接使用本地处理器中的寄存器,或者直接从本地内存的堆中分配任意数量的内存等等。总之,它和虚拟机拥有同样的权限(或者说能力)。

本地方法本质上是依赖于实现的,虚拟机实现的设计者们可以自由地决定使用怎样的机制来让Java程序调用本地方法。

任何本地方法接口都会使用某种本地方法栈。当线程调用Java方法时,虚拟机会创建一个新的栈帧并压入Java栈。然而当它调用的是本地方法时,虚拟机会保持Java栈不变,不再在线程的Java栈中压入新的帧,虚拟机只是简单地动态链接并直接调用指定的本地方法。可以把这看作是虚拟机利用本地方法来动态扩展自己。就如共Java虚拟机是的实现在按照其中运行的Java程序的吩咐,调用属于虚拟机内部的另一(动态链接的)方法。

如果某个虚拟机实现的本地方法接口是使用C连接模型的话,那么它的本地方法栈就是C栈。我们知道,当C程序调用一个C函数时,其栈操作都是确定的。传递给该函数的参数以某个确定的顺序压入栈,它的返回值也以确定的方式传回调用者。同样,这就是该虚拟机实现中本地方法栈的行为。

很可能本地方法接口需要回调Java虚拟机中的Java方法(这也是由设计者决定的),在这种情形下,该线程会保存本地方法栈的状态并进入到另一个Java栈........

 

  jvm规范中描述的是:所有的对象示例以及数组都要在堆上分配,但是随着JIT编译器的发展和逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化,所有的对象都分配在堆上也渐渐变得不是那么“绝对”了。垃圾收集器管理的主要区域就是堆了,从内存回收的角度上,由于现在收集器基本上采用分代收集算法,所以java堆还可以分为:新生代和老年代。

 很多人都愿意把方法区叫做永久代,本质上两者并不相等,仅仅是因为HotSopt虚拟机的设计团队选择把GC分代收集扩展至方法区,或者说使用永久代来实现方法区而已,这样HotSopt的垃圾收集器可以像管理Java堆一样管理这部分内存,能够省去专门为方法区编写的内存管理代码工作。 原则上,如何实现方法区属于虚拟机的实现细节,但使用永久代来实现方法区,现在看来更容易遇到内存溢出的问题。

 

 

运行时常量池

 他是方法区的一部分,Class文件中除了类的版本,字段,方法,接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用。

  

 

 

 

 

posted @ 2014-04-08 23:52  老邝  阅读(210)  评论(0编辑  收藏  举报