Java内存管理机制-1

来源:互联网 发布:js中的prop 编辑:程序博客网 时间:2024/06/12 01:26

学习了一下java内存管理机制 (深入理解Java虚拟机:JVM高级特性和最佳实践 ——周志明)


Java运行时数据区域:

          Java虚拟机在执行java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域都有各自的用途,及其创建和销毁的时间。有的区域随着虚拟机的启动而存在,有些区域则是依赖用户线程的启动而建立结束而销毁。

          运行时数据区域包含了:方法区(Method Area)

                                                      虚拟机栈(VM stack)

                                                      本地方法栈(native Method stack)

                                                      堆(heap) 

                                                      程序计数器 (program Counter register)

       程序计数器(program counter register):

                           程序计数器是一个比较小的内存区域,可以看成是当前线程所执行的字节码的行号指示器。字节码解释器就是通过改变计数器的值来选取下一条需要执行的字节码指令。每个线程都有一个独立的程序计数器,各个线程之间相互独立不影响,所以程序计数器可以说是线程私有的如果线程正在执行一个java方法,那么程序计数器的存放的是正在执行的虚拟机字节码指令的地址。如果正在执行native 方法则程序计数器为空。

     

       虚拟机栈(VM stack):

                          虚拟机栈也是线程私有的它的生命周期与线程相同。虚拟机栈描述的是java方法执行的内存模型:每个方法执行的时候都会创建一个栈帧(stack frame)用于存储局部变量表、操作栈,、动态链接、方法出口等信息。一个方法被调用到执行完成的过程就对应着一个栈帧进栈和出栈的过程。平时程序员所说的栈就是这个虚拟机栈。或者说是虚拟机栈中的局部变量表部分。

                         局部变量表存放了编译期可知的基本数据类型(boolean ,char short int  long float double byte )、对象引用、returnaddress(指向一条字节码指令的地址)64位的long 和double会占用两个局部变量空间,其余的只会占用一个。局部变量表所需要的内存空间会在编译期间就完成分配,当进入一个方法的时候,这个方法需要在帧中分配多大的空间是完全确定的,在方法的运行期间是不会改变局部变量表的大小的。

                        异常:如果线程请求的栈深度大于虚拟机所允许的深度,将会抛出StackOverFlowError异常

                                    也有可以扩展的栈深度的虚拟机,当扩展时无法申请到足够的内存时候就会抛出OutOfMenoryError异常

        本地方法栈(native method stack ):

                        本地方法栈与虚拟机栈所产生的作用是非常相似的,区别是:虚拟机栈为虚拟机执行java方法提供服务,而本地方法栈则是为虚拟机使用本地方法提供服务。

        

         堆(Heap):

                          堆可以说是java虚拟机管理内存中最大的一块内存区域。java堆是一块线程共享的一块内存区域,并区域不是线程私有的,在虚拟机启动的时候创建。堆的唯一目的是存放对象的实例。几乎所有的对象实例都在这里分配空间。堆可以处在物理上不连续的空间中,只要逻辑上连续也是可以的。

       

         方法区(Method Area):

                          方法区和堆一样也是线程共享的一块内存区域。方法区的作用是存储已经被虚拟机加载的类的信息,常量,静态变量、即时编译器编译后的代码等数据,这块区域也是不需要物理内存的连续,逻辑连续即可,也可以选择固定的大小也可以扩展。

             

          运行时常量池(runtime constant pool):

                          运行时常量池是方法区的一部分,.class文件中除了有类的版本,字段,方法,接口等描述信息外,还有一项信息就是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放子啊常量池中。

          

           上面这些大概是有关java内存的最基本的一个概念了。下面看下对象的访问,内存是怎么使用的。

            Object obj = new Object();

         假设这个句代码出现在方法体中,那么“Object  obj”这部分的定义(对象引用)将会存放到java栈的本地变量表中,作为一个对象引用类型出现。“new Object()”这部分的定义将会反映到堆中,形成一块存储Object类型的所有实例数值的结构化内存,虚拟机栈存放的对象引用到找到堆里面存放的对象实例有两种主流的寻找方式第一是使用句柄,第二是直接指针

          如果是使用的句柄访问方式,java堆中将会划分出一块内存来作为句柄池,对象引用存储的就是对象的句柄地址,而句柄中包含了对象实例数据和类型数据各自的具体地址信息。

            


   如果使用直接指针的访问方式:java堆对象的的布局中就碧玺考虑如何访问类型数据相关的信息,对象引用存储的就是对象的地址

         

       这两种对象的访问方式各有优势,

                  1、使用句柄方式的时候最大的好处就是对象引用中存储的是稳定的句柄地址,在对象被移动的时候只会改变句柄中的实例数据指针而对象引用本身不会改变。

                  2、使用直接指针方法的最大好处就是速度快,节省了一次指针定位的时间开销,对象访问在java中是非常频繁的,这也是能节省一笔非常客观的执行成本













       


0 0
原创粉丝点击