JVM——001内存划分

       

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

Java虚拟机运行时内存划分

JVM——001内存划分

程序计数器

    程序计数器是一块较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器。在因为Java虚拟机是通过线程轮流切换并分配处理器执行时间的方式来实现的。在任何一个确定的时刻,一个处理器都会执行一条线程中的指令。所以就要求每个线程有自己的程序计数器。每个线程之间的计数器相互独立。这块内存区域成为线程私有的内存。
    线程在执行的是一个Java方法,这个计数器记录的就是正在执行虚拟机字节码指令的地址。如果是Native方法,这个计数器的值就位空。所谓Native方法简单来说就是Java需要调用底层的一些接口,具体实现是非Java语言来实现的,可以是C,C++或者其他方法什么的。在此内存区域式唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

    Java虚拟机栈

Java虚拟机栈也是线程私有的。每个方法在执行的同时会创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每个方法从调用直到执行完成,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
    虚拟机栈一般存放编译期间可知的各种基本数据类型,以及对象引用和returnAddress类型。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的。在方法运行期间不会改变局部变量表的大小。
    在Java虚拟机规范中,该区域规定了两种异常情况,线程请求的栈深度大于虚拟机允许的深度,就会抛出StackOverflowError异常。如果虚拟机在动态扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

 本地方法栈

本地方法栈和虚拟机栈的作用非常相似,只不过虚拟机栈是执行Java方法服务,而本地方法栈是虚拟机使用到的Native方法服务。

    Java堆

Java堆是GC主要管理的区域。也是整个内存最大的一块区域。在运行期间,堆被所有的线程共享,虚拟机启动时创建,几乎所有的对象实例都在这里分配内存。包括数组。但是随着一些技术的发展,也发生了稍许的变化,所有的对象都分配在堆上,不再那么绝对了。
     Java堆可以处于不连续的内存空间中,只要逻辑上是连续的即可。如果实际所需的堆超过了自动内存管理系统能提供的最大容量,那Java虚拟机将会抛出一个OutOfMemoryError异常。

  方法区

方法区是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息,常量,静态变量。所以方法区也是我们在编写代码时的一个重要区域。

    运行时常量池
 运行常量池是方法区的一部分,Class文件除了有类的版本,字段,方法,接口等描述信息外,还有一项信息是常量池,用于存放各种编译期间生成的字面量和符号引用。这部分在类加载后进入方法区的运行时常量池中存放。
    
    直接内存区
直接内存区并不是虚拟机运行时数据的一部分,也不是Java虚拟机规范中定义的内存区域。但是这部分内存被频繁的使用,也会导致OutOfMemoryError异常出现。
    本机直接内存的分配不会受到Java堆大小的限制,但是会受到本机总内存大小以及处理器寻址空间的限制。

    Java中对象的创建对于以上的内存的分配,从一个方法体中的对象的创建说起
                         Object obj = new Object()
    首先虚拟机在执行过程中遇到new关键字后,首先检查在常量池中能否定位到该类的加载信息,如果不能定位到该类的加载信息,就会加载该类的信息,然后在去实例化对象。由于是在方法体内执行,所有会用到虚拟机栈,在虚拟机栈中分配空间,保存obj引用地址,然后再去Java堆中开辟空间,生成一个Object对象。至于该对象的一些数据类型信息等直接保存在方法区上。
    对于Java堆内的对象访问机制,不同的虚拟机有不同的实现方式。主流的访问方式有两种:使用句柄池和直接使用指针。
JVM——001内存划分
JVM——001内存划分

    这两种对象访问方式各有优势,使用句柄来访问的最大好处就是reference中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而reference本身不需要修改。
    使用直接指针访问方式的最大好处就是速度更快,它节省了一次指针定位的时间开销,由于对象的访问在Java中非常频繁,因此这类开销积少成多后也是一项非常可观的执行成本。

    总结: 基于小编我目前对Java代码的理解,应该多关注下方法区和Java堆。因为他们是所有线程共享的区域。既然共享了可能就会资源竞争的问题。所以要小心。另一方面对于服务器等资源分配,在代码里不用的资源尽量释放掉。防止内存溢出。