阅读 68

jvm运行时数据区表示

方法区的概念

根据JVM的规范,方法区用来存储类的结构,比如运行时常量池,字段和方法数据,方法和构造函数的代码,以及类和实例初始化,接口初始化中使用的一些特殊方法。规范把方法区从逻辑上看做是属于堆的一部分,不同的实现可以选择是否要对这块代码做垃圾回收和压缩,但是虚拟机规范本身并不强制规定方法区的位置(JDK 7 规范 JDK 8 规范 JDK 9 规范 JDK 10 规范 JDK 11 规范 在这一点上都是如此)。也就是说不同的实现可以放在不同的地方

以下来自hotspot

PermGem的概念

Hostspot中特有,它是方法区的一种实现。在jdk 7中,通过 -XX:MaxPermSize来决定最大的永久代大小,-XX:PermSize来控制方法区的初始大小。注意这里讲到 PermGem 是指permanent generation,它是从垃圾回收的角度来产生了一个generation的概念

在JVM中,有些对象很快被回收,有的则相对比较慢,为了优化不同的对象回收的场景,就提出分代处理的概念 Generation详见

方法区与“堆”的关系

要看“堆”的上下语境。

  • 如果堆指的是 GC heap,那么它就是在堆中,值得注意的是,如果说 Native memory,这里所指的角度就是 GC heap。
  • 如果这个堆是从广义上讲java heap的定义:哪儿存储了java对象,哪儿就是 java heap,那它就是在堆中 (java.lang.String的一些实例会存在这里)
  • 如果堆指的是运行时数据区中,用来分配类实例和数组的这块区域,那么它就不再堆中,从这个角度讲它也称作 non-heap

常量池与PermGen的关系

常量池有很多种,要看常量池的角度

比如字符串常量,Integer常量,全量地址戳这里

  • 如果是 runtime constant pool,它还在PermGen里面
  • 如果是 SymbolTable、StringTable,它们一直存在 native memory
  • 如果是说 SymbolTable、StringTable的引用,jdk 7开始,SymbolTable引用的Symbol移动到了native memory,而StringTable则移到了普通的hava heap

StringTable 就是通过hash存储通常的String的字符串常量表 ;SymbolTable用来保存定位和重新定位符号定义和符号引用所需要的信息

运行时常量池 表示类文件中每个类或者每个接口运行时的表示,它包括编译时已知的数字常量到必须在运行时解析的方法和字段引用

jdk8 对PermGen 的改进

hotspot中移除了PermGen,使用Metaspace,可以使用-XX:MetaspaceSize-XX:MaxMetaspaceSize配置

permGen,“heap”,常量池之间的关系参考

栈帧

帧用来存储数据和部分结果,包括动态连接、方法返回值和打包异常。 一个新的帧会在方法执行的时候创建,并在方法执行完毕的时候销毁。每个帧都会包含自己的局部变量,操作数栈和类当前运行方法对运行时常量池的引用。在编译的时候,局部变量和操作数栈的大小就定下来了。任何时候,给定一个线程只有一个帧是Active,它又被称作当前帧

线程自己创建的帧是不能和其它线程共享的

局部变量

局部变量通过下标索引的方式访问。第一个局部变量的索引是0,方法执行过程中的传参也是使用局部变量来实现的,他们从0开始一直按照递增的方式连续的增长下标表示不同的参数。特别的下标0永远表示传递的对象的引用,在java中就是 this

操作数栈

每个帧都包含了一个 后进先出 的栈,包含操作数的帧刚建立的时候,它是空的,JVM会提供指令来把常量、字段值、局部变量加载如栈,然后由其它的指令取出并操作,然后把结果放回到栈中

传递给下一个方法的参数和接收方法的返回值也都是放在这里

操作数栈中的每一个值的类型和操作方法一定是匹配上的,这种关系在class文件上会做验证。在任意的时刻,栈本身都会对应着栈的深度,它支持JVM中的任何类型,除去long和double会占据两个单元,其它类型都只占据一个单元

动态连接

class文件的代码中,对要执行的方法和变量都是通过符号引用获取的,动态连接负责把这些符号引用转换成对应的方法引用,加载那些还未定义过的符号,并把变量运行时的位置转换成存储结构中正确偏移处。

通过维持常量池的引用就可以达到这种动态连接的实现