JVM内存结构

PC寄存器
  严格来说这是一个数据结构,用于保存当前正常执行的程序的内存地址。同时Java程序是多线程执行的,所以不可能一直按照线性执行下去,当有多个线程交叉执行时,被中断线程的程序当前执行到哪条的内存地址必然要保存下来,以便于它被恢复执行再按照被中断时的指令地址继续执行下去。这很好理解,它就像一个记事员一样记录下哪个线程当前执行到哪条指令了。这仅限于Java方法,对于Native方法并没有要求记录执行的指针地址。

Java栈
  每当创建一个线程时,JVM就会为这个线程创建一个对应的Java栈,在这个Java栈中又会含有多个栈帧,这些栈帧是与每个方法关联起来的,没运行一个方法就创建一个栈帧,每个栈帧会含有一些内部变量、操作栈和方法返回值等信息。每当一个方法执行完成时,这个栈帧就会弹出栈帧的元素作为这个方法的返回值,并清除这个栈帧,Java栈的栈顶的栈帧就是当前正在执行的活动栈,也就是当前正在执行的方法,PC寄存器也会指向这地址。只有中国活动的栈帧的本地变量可以被操作栈使用,当这个栈帧中调用另外一个方法时,与之对应的一个新的栈帧的又被创建,这新创建的栈帧又被放到Java栈的顶部,变为当前的活动栈帧。Java栈是与Java线程对应起来的,这个数据不是共享的,所以不用关心数据一致性问题,也不会存在同步锁的问题。


  存储Java对象的地方,是JVM管理Java对象的核心存储区域,也是程序员最应该关心的地方。每一个存储在堆中的对象都会是这个对象的类的一个副本,他会复制包括继承自它父类的所有非静态属性。堆是所有Java线程共享的,因此对堆的访问需要注意数据同步的问题,方法和对应的属性都需要保证一致性。

方法区(java8被取消,由元数据区替代)
  用于存储类结构信息的地方,class文件中会标记出JVM能识别的不同的几个部分,这些不同的部分在这个class被加载到JVM时,会被存储在不同的数据结构中,其中的常量池、域、方法数据、方法体、构造函数,包括类中的专用方实例初始化、接口初始化都存在方法区。这个区域其实属于Java堆中的一部分(也就是说仍然会被GC管理),是Java堆中的永久区,它的大小可以通过参数来设置。
  这个方法区存储区域的大小在程序启动后的一段时间内就固定了,JVM运行一段时间后,需要加载的类通常都已经加载到JVM中了。需要注意一种情况,项目中如果存在对类的动态编译,而且是同样一个类的多次编译,那么需要观察方法区的大小是否能满足类存储。

运行时常量池
  这个常量池是方法区的一部分,就是上面说的方法区中的常量池。这里的常量包括:编译其的数字常量、方法或者域的引用(运行时解析)。每个常量池都是在JVM的Method area中分配的,每个Class或者Interface的Constant Pool都是在JVM创建class或接口时创建的。

本地方法栈
  本地方法栈是为JVM运行Native方法准备的空间,它和前面介绍的Java栈的作用类似的,由于很多Native方法都是用C语言实现的,所有它通常又叫C栈,除了在我们的代码中包含的常规的Native方法会使用这个存储空间,在JVM利用JIT技术时会将一些Java方法重新编译为Native Code,这些编译后的本地代码通常也是利用这栈来跟踪方法的执行状态的。
  JVM规范中没有对这个区域的严格限制,它可以由不用的JVM实现者自由实现,和其他存储区一样也会抛出OutOfMemoryError和StackOverflowError。

猜你喜欢

转载自blog.csdn.net/u012484172/article/details/79971539