zoukankan      html  css  js  c++  java
  • Java虚拟机内存模型

    在Java应用开发过程中,我们经常会遇到OutOfMemoryError异常,而不知道是啥原因,此时就要知道Java的内存模型啦,找了些资料,把Java内存模型总结一下;Java的内存结构,也就是运行时的数据区域;Java虚拟机在执行Java程序的过程中,会把它管理的内存划分为几个不同的数据区域,这些数据区域都有各自的用途,创建时间销毁时间,Java运行时的数据区域可以大致分为如下几个区域:

    1.PC寄存器/程序计数器(线程私有)

            程序计数器是一块较小的内存空间,他可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令;分支,循环,跳转,异常处理,线程恢复等基础功能都需要依赖这个计数器来完成。

            由于Java虚拟机的多线程是通过多线程的轮流切换分配处理器执行时间的方式来实现的,在任何一个时刻,一个处理器(多核处理器来说是一个内核)都只会处理一条线程中的指令,因此,为了线程切换后能恢复到正确的执行位置,每个线程都需要有一个独立的程序计数器,各个线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存,这在某种程度上有点类似于“ThreadLocal”,是线程安全的。

            此内存区域是唯一一个在Java虚拟机中没有规定任何OutOfMemoryError异常的区域。

    2.Java栈(线程私有)

            与程序计数器一样,Java虚拟机栈也是线程私有的,它的生命周期于线程相同。Java栈总是与线程关联在一起的,每当创建一个线程,JVM就会为该线程创建对应的Java栈,在这个Java栈中又会包含多个栈帧(Stack Frame),这些栈帧是与每个方法关联起来的,每运行一个方法就创建一个栈帧,每个栈帧会含有一些局部变量、操作栈和方法返回值等信息。每当一个方法执行完成时,该栈帧就会弹出栈帧的元素作为这个方法的返回值,并且清除这个栈帧,Java栈的栈顶的栈帧就是当前正在执行的活动栈,也就是当前正在执行的方法,PC寄存器也会指向该地址。每执行为一个方法,就对应着一个栈帧在虚拟机栈中的“入栈”与“出栈”过程。

      由于Java栈是与线程对应起来的,Java栈数据不是线程共有的,所以不需要关心其数据一致性,也不会存在同步锁的问题。

      在Java虚拟机规范中,对这个区域规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果虚拟机可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。在Hot Spot虚拟机中,可以使用-Xss参数来设置栈的大小。栈的大小直接决定了函数调用的可达深度。

    3.本地方法栈(线程私有)

            本地方法栈和Java虚拟机栈所发挥的作用非常相似,区别不过是Java栈为JVM执行Java方法服务,而本地方法栈为JVM执行Native方法服务。本地方法栈也会抛出StackOverflowError和OutOfMemoryError异常。

    4.Java堆(线程共享)

            堆是JVM所管理的内存中最大的一块,是被所有Java线程所共享的,不是线程安全的,在JVM启动时创建。堆是存储Java对象的地方,这一点Java虚拟机规范中描述是:所有的对象实例以及数组都要在堆上分配。Java堆是GC管理的主要区域,从内存回收的角度来看,由于现在GC基本都采用分代收集算法,所以Java堆还可以细分为:新生代和老年代;如果在堆中没有内存完成实例分配,并且堆也无法拓展时,将会跑出OutOfMemoryError异常。

    5.方法区(线程共享)

            方法区存放了要加载的类的信息(名称、修饰符等)、类中的静态常量、类中定义为final类型的常量、类中的Field信息、类中的方法信息,当在程序中通过Class对象的getName.isInterface等方法来获取信息时,这些数据都来源于方法区。方法区是被Java线程锁共享的,不像Java堆中其他部分一样会频繁被GC回收,它存储的信息相对比较稳定,在一定条件下会被GC,当方法区要使用的内存超过其允许的大小时,会抛出OutOfMemory的错误信息。方法区也是堆中的一部分,就是我们通常所说的Java堆中的永久区 Permanet Generation,大小可以通过参数来设置,可以通过-XX:PermSize指定初始值,-XX:MaxPermSize指定最大值。

            常量池:常量池本身是方法区中的一个数据结构。常量池中存储了如字符串、final变量值、类名和方法名常量。常量池在编译期间就被确定,并保存在已编译的.class文件中。一般分为两类:字面量和应用量。字面量就是字符串、final变量等。类名和方法名属于引用量。引用量最常见的是在调用方法的时候,根据方法名找到方法的引用,并以此定为到函数体进行函数代码的执行。引用量包含:类和接口的权限定名、字段的名称和描述符,方法的名称和描述符。

  • 相关阅读:
    fastjson反序列化
    BaseBean构造
    easyUI_rowdata
    SVN提交报错
    jsp页面数据保留两位小数
    数据库 建立
    增删改查问
    解决plsql中文乱码问题
    oracle_解锁表
    ibatis_compareValue
  • 原文地址:https://www.cnblogs.com/cowboys/p/9278954.html
Copyright © 2011-2022 走看看