潜入java内存结构

java的内存结构:

  在并发编程中,多个线程之间采用共享内存模型来实现多线程之间的信息交换和数据同步.线程之间通过共享程序公共的状态,通过读-写内存中公共状态的方式来进行隐式的通信.同步指的是程序在控制多个线程之间执行程序的相对顺序的机制.

java内存结构图:

  

java 运行时数据区:

  1,PC寄存器/程序计数器:

    程序计数器本身是一个数据结构,用于保存当前正在执行的程序内存地址,由于java是支持多线程执行,所以程序执行的轨迹不可能一直都是线性执行.当多个线程交叉执行时,被中断的程序当前执行到哪个内存地址必然要保存下来,以便用于程序的恢复.为了线程切换后能恢复到正确的执行位置,每个线程都需要有一个独立的程序计数器,各个线程之间计数器互不影响,独立存储,我们称这类内存区域为"线程私有"的内存,是线程安全的.有点类似"ThreadLocal"类.

  2,java栈Java Stack

    java栈总是与线程关联起来,每当创建一个线程,JVM就会为该线程创建对应的java栈,在这个java栈中又会包含多个栈帧(Stack Frame),这些栈帧是与每个方法关联起来的,每运行一个方法就创建一个栈帧,每个栈帧会含有一些局部变量、操作栈和方法返回值等信息.每当一个方法执行完成时,该栈帧就会弹出栈帧的元素作为这个方法的返回值,并且清除这个栈帧,java栈的栈顶的栈帧就是当前正在执行的活动栈,也就是当前正在执行的方法,PC寄存器也会指向该地址.只有这个活动的栈帧的本地变量可以被操作栈使用,当这个栈帧中调用另外一个方法时,与之对应的一个新栈帧被创建,这个新创建的栈帧被放到java栈的栈顶,变为当前的活动栈.同样现在只有这个栈的本地变量才能被使用,当这个栈中所有指令都完成时,这个栈帧被移除java栈,刚才的那个栈帧变为活动栈帧,前面栈帧的返回值变为这个栈帧的操作栈的一个操作数.

    java栈是与线程对应起来,java栈数据不是线程共有的,所有不需要关系数据不一致性,也不会出现同步锁的问题.对这个区域规定有两种异常状况:

      a,如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;
      b,如果虚拟机可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常.

    在虚拟机中可以使用-Xss参数来设置栈的大小(-Xss:设置每个线程的堆栈大小)

  3,java 堆Head:

    堆是JVM所管理最大的一块内存空间,是被所有java线程所共享的,不是线程安全的,在JVM启动时创建.堆是存储java对象的地方,所有的对象实例以及数组都要在堆上分配.java堆是GC管理的主要区域,从内存回收的角度来看,由于现在GC基本都是采用分代收集算法,所以java堆还可以分为:新生代和老年代,新生代又可以分为:一个Eden空间和两个Survivor空间.

    堆大小设置:

      通过-Xmx与-Xms参数控制堆的最大和最小内存空间.新生代+老年代+持久代=JVM内存大小,持久代一般固定为64M,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。

  4,方法区Method Area:

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

  5,本地方法栈Native Method Stack

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

 

posted @ 2018-12-11 10:09  凡城似锦  阅读(106)  评论(0)    收藏  举报